From c07c57ca2ae1d1dae0722bc33a8edeb0aea75bc6 Mon Sep 17 00:00:00 2001 From: Aristo7 <5432499+Aristo7@users.noreply.github.com> Date: Tue, 20 Apr 2021 12:53:46 -0500 Subject: [PATCH] Revert "Removed HLSL cry compilers and tools" This reverts commit f8a72e5a040c62a5c489a87c8ad39f2256b9153a. --- Code/Tools/CMakeLists.txt | 2 + Code/Tools/CryFXC/cryfxc.sln | 26 + Code/Tools/CryFXC/cryfxc/cryfxc.cpp | 494 ++ Code/Tools/CryFXC/cryfxc/cryfxc.vcxproj | 153 + Code/Tools/CryFXC/cryfxc/stdafx.cpp | 14 + Code/Tools/CryFXC/cryfxc/stdafx.h | 29 + Code/Tools/CryFXC/cryfxc/targetver.h | 16 + Code/Tools/HLSLCrossCompiler/CMakeLists.txt | 57 + .../Platform/Linux/platform_linux.cmake | 10 + .../Platform/Mac/platform_mac.cmake | 11 + .../Platform/Windows/platform_windows.cmake | 18 + Code/Tools/HLSLCrossCompiler/README | 71 + .../HLSLCrossCompiler/hlslcc_files.cmake | 60 + .../hlslcc_header_files.cmake | 18 + .../include/amazon_changes.h | 13 + Code/Tools/HLSLCrossCompiler/include/hlslcc.h | 580 ++ .../HLSLCrossCompiler/include/hlslcc.hpp | 7 + .../HLSLCrossCompiler/include/hlslcc_bin.hpp | 419 ++ .../Tools/HLSLCrossCompiler/include/pstdint.h | 801 +++ Code/Tools/HLSLCrossCompiler/jni/Android.mk | 32 + .../HLSLCrossCompiler/jni/Application.mk | 3 + .../lib/android-armeabi-v7a/libHLSLcc.a | 3 + .../lib/ios-arm64/libHLSLcc.a | 3 + .../lib/ios-simx86_64/libHLSLcc.a | 3 + .../HLSLCrossCompiler/lib/ios/libHLSLcc.a | 3 + .../HLSLCrossCompiler/lib/linux/libHLSLcc.a | 3 + .../HLSLCrossCompiler/lib/linux/libHLSLcc_d.a | 3 + .../HLSLCrossCompiler/lib/mac/libHLSLcc.a | 3 + .../HLSLCrossCompiler/lib/mac/libHLSLcc_d.a | 3 + .../HLSLCrossCompiler/lib/steamos/libHLSLcc.a | 3 + .../lib/steamos/libHLSLcc_d.a | 3 + .../HLSLCrossCompiler/lib/win32/libHLSLcc.lib | 3 + .../HLSLCrossCompiler/lib/win64/libHLSLcc.lib | 3 + Code/Tools/HLSLCrossCompiler/license.txt | 53 + .../HLSLCrossCompiler/offline/cjson/README | 247 + .../HLSLCrossCompiler/offline/cjson/cJSON.c | 578 ++ .../HLSLCrossCompiler/offline/cjson/cJSON.h | 142 + .../offline/compilerStandalone.cpp | 803 +++ Code/Tools/HLSLCrossCompiler/offline/hash.h | 152 + .../offline/serializeReflection.cpp | 207 + .../offline/serializeReflection.h | 11 + .../Tools/HLSLCrossCompiler/offline/timer.cpp | 40 + Code/Tools/HLSLCrossCompiler/offline/timer.h | 29 + .../HLSLCrossCompiler/src/amazon_changes.c | 219 + .../HLSLCrossCompiler/src/cbstring/bsafe.c | 20 + .../HLSLCrossCompiler/src/cbstring/bsafe.h | 45 + .../HLSLCrossCompiler/src/cbstring/bstraux.c | 1134 ++++ .../HLSLCrossCompiler/src/cbstring/bstraux.h | 113 + .../HLSLCrossCompiler/src/cbstring/bstrlib.c | 2976 +++++++++ .../HLSLCrossCompiler/src/cbstring/bstrlib.h | 305 + .../src/cbstring/bstrlib.txt | 3201 ++++++++++ .../src/cbstring/license.txt | 29 + .../src/cbstring/porting.txt | 172 + .../src/cbstring/security.txt | 221 + Code/Tools/HLSLCrossCompiler/src/decode.c | 1845 ++++++ Code/Tools/HLSLCrossCompiler/src/decodeDX9.c | 1113 ++++ .../HLSLCrossCompiler/src/hlslccToolkit.c | 167 + .../src/internal_includes/debug.h | 21 + .../src/internal_includes/decode.h | 21 + .../src/internal_includes/hlslccToolkit.h | 35 + .../src/internal_includes/hlslcc_malloc.c | 16 + .../src/internal_includes/hlslcc_malloc.h | 15 + .../src/internal_includes/languages.h | 242 + .../src/internal_includes/reflect.h | 42 + .../src/internal_includes/shaderLimits.h | 36 + .../src/internal_includes/structs.h | 374 ++ .../src/internal_includes/toGLSLDeclaration.h | 19 + .../src/internal_includes/toGLSLInstruction.h | 18 + .../src/internal_includes/toGLSLOperand.h | 46 + .../internal_includes/toMETALDeclaration.h | 16 + .../internal_includes/toMETALInstruction.h | 18 + .../src/internal_includes/toMETALOperand.h | 38 + .../src/internal_includes/tokens.h | 812 +++ .../src/internal_includes/tokensDX9.h | 304 + Code/Tools/HLSLCrossCompiler/src/reflect.c | 1075 ++++ Code/Tools/HLSLCrossCompiler/src/toGLSL.c | 1921 ++++++ .../HLSLCrossCompiler/src/toGLSLDeclaration.c | 2908 +++++++++ .../HLSLCrossCompiler/src/toGLSLInstruction.c | 5598 +++++++++++++++++ .../HLSLCrossCompiler/src/toGLSLOperand.c | 2121 +++++++ .../HLSLCrossCompilerMETAL/CMakeLists.txt | 54 + .../Platform/Linux/PAL_linux.cmake | 12 + .../Platform/Mac/PAL_mac.cmake | 12 + .../Platform/Windows/PAL_windows.cmake | 12 + Code/Tools/HLSLCrossCompilerMETAL/README | 52 + .../bin/win32/HLSLcc.exe | 3 + .../bin/win32/HLSLcc_d.exe | 3 + .../hlslcc_metal_files.cmake | 65 + .../HLSLCrossCompilerMETAL/include/hlslcc.h | 537 ++ .../HLSLCrossCompilerMETAL/include/hlslcc.hpp | 7 + .../include/hlslcc_bin.hpp | 448 ++ .../HLSLCrossCompilerMETAL/include/pstdint.h | 801 +++ .../HLSLCrossCompilerMETAL/jni/Android.mk | 32 + .../HLSLCrossCompilerMETAL/jni/Application.mk | 3 + .../lib/android-armeabi-v7a/libHLSLcc.a | 3 + .../lib/ios/libHLSLcc.a | 3 + .../lib/linux/libHLSLcc.a | 3 + .../lib/linux/libHLSLcc_d.a | 3 + .../lib/mac/libHLSLcc.a | 3 + .../lib/mac/libHLSLcc_d.a | 3 + .../lib/steamos/libHLSLcc.a | 3 + .../lib/steamos/libHLSLcc_d.a | 3 + .../lib/win32/Debug/libHLSLcc.lib | 3 + .../lib/win32/Release/libHLSLcc.lib | 3 + .../lib/win32/libHLSLcc.lib | 3 + .../lib/win64/Release/libHLSLcc.lib | 3 + .../lib/win64/libHLSLcc.lib | 3 + Code/Tools/HLSLCrossCompilerMETAL/license.txt | 52 + .../offline/cjson/README | 247 + .../offline/cjson/cJSON.c | 578 ++ .../offline/cjson/cJSON.h | 142 + .../offline/compilerStandalone.cpp | 825 +++ .../HLSLCrossCompilerMETAL/offline/hash.h | 128 + .../offline/serializeReflection.cpp | 207 + .../offline/serializeReflection.h | 11 + .../HLSLCrossCompilerMETAL/offline/timer.cpp | 40 + .../HLSLCrossCompilerMETAL/offline/timer.h | 29 + .../src/cbstring/bsafe.c | 20 + .../src/cbstring/bsafe.h | 39 + .../src/cbstring/bstraux.c | 1134 ++++ .../src/cbstring/bstraux.h | 113 + .../src/cbstring/bstrlib.c | 2976 +++++++++ .../src/cbstring/bstrlib.h | 305 + .../src/cbstring/bstrlib.txt | 3201 ++++++++++ .../src/cbstring/license.txt | 29 + .../src/cbstring/porting.txt | 172 + .../src/cbstring/security.txt | 221 + .../Tools/HLSLCrossCompilerMETAL/src/decode.c | 1750 ++++++ .../HLSLCrossCompilerMETAL/src/decodeDX9.c | 1133 ++++ .../src/internal_includes/debug.h | 21 + .../src/internal_includes/decode.h | 18 + .../src/internal_includes/hlslcc_malloc.c | 37 + .../src/internal_includes/hlslcc_malloc.h | 15 + .../src/internal_includes/languages.h | 213 + .../src/internal_includes/reflect.h | 73 + .../src/internal_includes/shaderLimits.h | 14 + .../src/internal_includes/structs.h | 338 + .../src/internal_includes/structsMETAL.c | 15 + .../src/internal_includes/structsMetal.h | 19 + .../src/internal_includes/toGLSLDeclaration.h | 19 + .../src/internal_includes/toGLSLInstruction.h | 18 + .../src/internal_includes/toGLSLOperand.h | 72 + .../internal_includes/toMETALDeclaration.h | 15 + .../internal_includes/toMETALInstruction.h | 20 + .../src/internal_includes/toMETALOperand.h | 78 + .../src/internal_includes/tokens.h | 819 +++ .../src/internal_includes/tokensDX9.h | 304 + .../HLSLCrossCompilerMETAL/src/reflect.c | 1213 ++++ .../Tools/HLSLCrossCompilerMETAL/src/toGLSL.c | 851 +++ .../src/toGLSLDeclaration.c | 2678 ++++++++ .../src/toGLSLInstruction.c | 4576 ++++++++++++++ .../src/toGLSLOperand.c | 1869 ++++++ .../HLSLCrossCompilerMETAL/src/toMETAL.c | 440 ++ .../src/toMETALDeclaration.c | 2281 +++++++ .../src/toMETALInstruction.c | 4946 +++++++++++++++ .../src/toMETALOperand.c | 2377 +++++++ 155 files changed, 71159 insertions(+) create mode 100644 Code/Tools/CryFXC/cryfxc.sln create mode 100644 Code/Tools/CryFXC/cryfxc/cryfxc.cpp create mode 100644 Code/Tools/CryFXC/cryfxc/cryfxc.vcxproj create mode 100644 Code/Tools/CryFXC/cryfxc/stdafx.cpp create mode 100644 Code/Tools/CryFXC/cryfxc/stdafx.h create mode 100644 Code/Tools/CryFXC/cryfxc/targetver.h create mode 100644 Code/Tools/HLSLCrossCompiler/CMakeLists.txt create mode 100644 Code/Tools/HLSLCrossCompiler/Platform/Linux/platform_linux.cmake create mode 100644 Code/Tools/HLSLCrossCompiler/Platform/Mac/platform_mac.cmake create mode 100644 Code/Tools/HLSLCrossCompiler/Platform/Windows/platform_windows.cmake create mode 100644 Code/Tools/HLSLCrossCompiler/README create mode 100644 Code/Tools/HLSLCrossCompiler/hlslcc_files.cmake create mode 100644 Code/Tools/HLSLCrossCompiler/hlslcc_header_files.cmake create mode 100644 Code/Tools/HLSLCrossCompiler/include/amazon_changes.h create mode 100644 Code/Tools/HLSLCrossCompiler/include/hlslcc.h create mode 100644 Code/Tools/HLSLCrossCompiler/include/hlslcc.hpp create mode 100644 Code/Tools/HLSLCrossCompiler/include/hlslcc_bin.hpp create mode 100644 Code/Tools/HLSLCrossCompiler/include/pstdint.h create mode 100644 Code/Tools/HLSLCrossCompiler/jni/Android.mk create mode 100644 Code/Tools/HLSLCrossCompiler/jni/Application.mk create mode 100644 Code/Tools/HLSLCrossCompiler/lib/android-armeabi-v7a/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/ios-arm64/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/ios-simx86_64/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/ios/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc_d.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc_d.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc_d.a create mode 100644 Code/Tools/HLSLCrossCompiler/lib/win32/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompiler/lib/win64/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompiler/license.txt create mode 100644 Code/Tools/HLSLCrossCompiler/offline/cjson/README create mode 100644 Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.c create mode 100644 Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.h create mode 100644 Code/Tools/HLSLCrossCompiler/offline/compilerStandalone.cpp create mode 100644 Code/Tools/HLSLCrossCompiler/offline/hash.h create mode 100644 Code/Tools/HLSLCrossCompiler/offline/serializeReflection.cpp create mode 100644 Code/Tools/HLSLCrossCompiler/offline/serializeReflection.h create mode 100644 Code/Tools/HLSLCrossCompiler/offline/timer.cpp create mode 100644 Code/Tools/HLSLCrossCompiler/offline/timer.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/amazon_changes.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.txt create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/license.txt create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/porting.txt create mode 100644 Code/Tools/HLSLCrossCompiler/src/cbstring/security.txt create mode 100644 Code/Tools/HLSLCrossCompiler/src/decode.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/decodeDX9.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/hlslccToolkit.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/debug.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/decode.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslccToolkit.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/languages.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/reflect.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/shaderLimits.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/structs.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLDeclaration.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLInstruction.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLOperand.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALDeclaration.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALInstruction.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALOperand.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/tokens.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/internal_includes/tokensDX9.h create mode 100644 Code/Tools/HLSLCrossCompiler/src/reflect.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/toGLSL.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/toGLSLDeclaration.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/toGLSLInstruction.c create mode 100644 Code/Tools/HLSLCrossCompiler/src/toGLSLOperand.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/CMakeLists.txt create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/Platform/Linux/PAL_linux.cmake create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/Platform/Mac/PAL_mac.cmake create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/Platform/Windows/PAL_windows.cmake create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/README create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc.exe create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc_d.exe create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/hlslcc_metal_files.cmake create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.hpp create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc_bin.hpp create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/include/pstdint.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/jni/Android.mk create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/jni/Application.mk create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/android-armeabi-v7a/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/ios/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc_d.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc_d.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc_d.a create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Debug/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Release/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/win32/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/win64/Release/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/lib/win64/libHLSLcc.lib create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/license.txt create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/README create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/compilerStandalone.cpp create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/hash.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.cpp create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/timer.cpp create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/offline/timer.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.txt create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/license.txt create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/porting.txt create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/security.txt create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/decode.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/decodeDX9.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/debug.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/decode.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/languages.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/reflect.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/shaderLimits.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structs.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMETAL.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMetal.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLDeclaration.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLInstruction.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLOperand.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALDeclaration.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALInstruction.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALOperand.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokens.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokensDX9.h create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/reflect.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toGLSL.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLDeclaration.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLInstruction.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLOperand.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toMETAL.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toMETALDeclaration.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toMETALInstruction.c create mode 100644 Code/Tools/HLSLCrossCompilerMETAL/src/toMETALOperand.c diff --git a/Code/Tools/CMakeLists.txt b/Code/Tools/CMakeLists.txt index 3cac4e7932..d2500dfd04 100644 --- a/Code/Tools/CMakeLists.txt +++ b/Code/Tools/CMakeLists.txt @@ -15,6 +15,8 @@ add_subdirectory(AWSNativeSDKInit) add_subdirectory(AzTestRunner) add_subdirectory(CryCommonTools) add_subdirectory(CryXML) +add_subdirectory(HLSLCrossCompiler) +add_subdirectory(HLSLCrossCompilerMETAL) add_subdirectory(News) add_subdirectory(PythonBindingsExample) add_subdirectory(RC) diff --git a/Code/Tools/CryFXC/cryfxc.sln b/Code/Tools/CryFXC/cryfxc.sln new file mode 100644 index 0000000000..26c5dc4e6d --- /dev/null +++ b/Code/Tools/CryFXC/cryfxc.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryfxc", "cryfxc\cryfxc.vcxproj", "{A505D345-D712-4C80-8BDE-6FBC08A390D8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Debug|Win32.Build.0 = Debug|Win32 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Debug|x64.ActiveCfg = Debug|x64 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Debug|x64.Build.0 = Debug|x64 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Release|Win32.ActiveCfg = Release|Win32 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Release|Win32.Build.0 = Release|Win32 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Release|x64.ActiveCfg = Release|x64 + {A505D345-D712-4C80-8BDE-6FBC08A390D8}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Code/Tools/CryFXC/cryfxc/cryfxc.cpp b/Code/Tools/CryFXC/cryfxc/cryfxc.cpp new file mode 100644 index 0000000000..0d7e3435dd --- /dev/null +++ b/Code/Tools/CryFXC/cryfxc/cryfxc.cpp @@ -0,0 +1,494 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +// Original file Copyright Crytek GMBH or its affiliates, used under license. + +#include "stdafx.h" + +#pragma comment(lib, "D3Dcompiler.lib") + +#define CRYFXC_VER "1.01" + + +enum SwitchType +{ + FXC_E, FXC_T, FXC_Help, FXC_CmdOptFile, FXC_Cc, FXC_Compress, FXC_D, FXC_Decompress, FXC_Fc, + FXC_Fh, FXC_Fo, FXC_Fx, FXC_P, FXC_Gch, FXC_Gdp, FXC_Gec, FXC_Ges, FXC_Gfa, FXC_Gfp, FXC_Gis, + FXC_Gpp, FXC_I, FXC_LD, FXC_Ni, FXC_NoLogo, FXC_Od, FXC_Op, FXC_O0, FXC_O1, FXC_O2, FXC_O3, + FXC_Vd, FXC_Vi, FXC_Vn, FXC_Zi, FXC_Zpc, FXC_Zpr, + + FXC_NumArgs +}; + + +struct SwitchEntry +{ + SwitchType type; + const char* text; + bool hasValue; + bool supported; +}; + + +const static SwitchEntry s_switchEntries[] = +{ + {FXC_E, "/E", 1, true}, + {FXC_T, "/T", 1, true}, + {FXC_Fh, "/Fh", 1, true}, + {FXC_Fo, "/Fo", 1, true}, + + {FXC_Gec, "/Gec", 0, true}, + {FXC_Ges, "/Ges", 0, true}, + {FXC_Gfa, "/Gfa", 0, true}, + {FXC_Gfp, "/Gfp", 0, true}, + {FXC_Gis, "/Gis", 0, true}, + {FXC_Gpp, "/Gpp", 0, true}, + {FXC_Od, "/Od", 0, true}, + {FXC_O0, "/O0", 0, true}, + {FXC_O1, "/O1", 0, true}, + {FXC_O2, "/O2", 0, true}, + {FXC_O3, "/O3", 0, true}, + {FXC_Op, "/Op", 0, true}, + {FXC_Vd, "/Vd", 0, true}, + {FXC_Vn, "/Vn", 1, true}, + {FXC_Zi, "/Zi", 0, true}, + {FXC_Zpc, "/Zpc", 0, true}, + {FXC_Zpr, "/Zpr", 0, true}, + {FXC_NoLogo, "/nologo", 0, true}, + + {FXC_Help, "/?", 0, false}, + {FXC_Help, "/help", 0, false}, + {FXC_Cc, "/Cc", 0, false}, + {FXC_Compress, "/compress", 0, false}, + {FXC_D, "/D", 1, false}, + {FXC_Decompress, "/decompress", 0, false}, + {FXC_Fc, "/Fc", 1, false}, + {FXC_Fx, "/Fx", 1, false}, + {FXC_P, "/P", 1, false}, + {FXC_Gch, "/Gch", 0, false}, + {FXC_Gdp, "/Gdp", 0, false}, + {FXC_I, "/I", 1, false}, + {FXC_LD, "/LD", 0, false}, + {FXC_Ni, "/Ni", 0, false}, + {FXC_Vi, "/Vi", 0, false} +}; + + +bool IsSwitch(const char* p) +{ + assert(p); + return *p == '/' || *p == '@'; +} + + +const SwitchEntry* GetSwitch(const char* p) +{ + assert(p); + for (size_t i = 0; i < sizeof(s_switchEntries) / sizeof(s_switchEntries[0]); ++i) + { + if (_stricmp(s_switchEntries[i].text, p) == 0) + { + return &s_switchEntries[i]; + } + } + + if (*p == '@') + { + const static SwitchEntry sw = {FXC_CmdOptFile, "@", 0, false}; + return &sw; + } + + return 0; +} + + +struct ParserResults +{ + const char* pProfile; + const char* pEntry; + const char* pOutFile; + const char* pInFile; + const char* pHeaderVariableName; + unsigned int compilerFlags; + bool disassemble; + + void Init() + { + pProfile = 0; + pEntry = 0; + pOutFile = 0; + pInFile = 0; + pHeaderVariableName = 0; + compilerFlags = 0; + disassemble = false; + } +}; + + +bool ParseCommandLine(const char* const* args, size_t numargs, ParserResults& parserRes) +{ + parserRes.Init(); + + if (numargs < 4) + { + fprintf(stderr, "Failed to specify all required arguments: infile, outfile, profile and entry point\n"); + return false; + } + + for (size_t i = 1; i < numargs; ++i) + { + if (IsSwitch(args[i])) + { + const SwitchEntry* sw = GetSwitch(args[i]); + if (!sw) + { + fprintf(stderr, "Unknown switch: %s\n", args[i]); + return false; + } + + if (!sw->supported) + { + fprintf(stderr, "Unsupported switch: %s\n", sw->text); + return false; + } + + if (sw->hasValue) + { + if (i + 1 == numargs || IsSwitch(args[i + 1])) + { + fprintf(stderr, "Missing value for switch: %s\n", sw->text); + return false; + } + + const char* pValue = args[i + 1]; + switch (sw->type) + { + case FXC_E: + parserRes.pEntry = pValue; + break; + case FXC_T: + parserRes.pProfile = pValue; + break; + case FXC_Fh: + parserRes.pOutFile = pValue; + parserRes.disassemble = true; + break; + case FXC_Fo: + parserRes.pOutFile = pValue; + break; + case FXC_Vn: + parserRes.pHeaderVariableName = pValue; + break; + default: + fprintf(stderr, "Failed assigning switch: %s | value: %s\n", sw->text, pValue); + return false; + } + + ++i; + } + else + { + switch (sw->type) + { + case FXC_Gec: + parserRes.compilerFlags |= D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY; + break; + case FXC_Od: + parserRes.compilerFlags |= D3D10_SHADER_SKIP_OPTIMIZATION; + break; + case FXC_O0: + parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL0; + break; + case FXC_O1: + parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL1; + break; + case FXC_O2: + parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL2; + break; + case FXC_O3: + parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL3; + break; + case FXC_Zi: + parserRes.compilerFlags |= D3D10_SHADER_DEBUG; + break; + case FXC_Zpc: + parserRes.compilerFlags |= D3D10_SHADER_PACK_MATRIX_COLUMN_MAJOR; + break; + case FXC_Zpr: + parserRes.compilerFlags |= D3D10_SHADER_PACK_MATRIX_ROW_MAJOR; + break; + case FXC_Ges: + parserRes.compilerFlags |= D3D10_SHADER_ENABLE_STRICTNESS; + break; + case FXC_Gfa: + parserRes.compilerFlags |= D3D10_SHADER_AVOID_FLOW_CONTROL; + break; + case FXC_Gfp: + parserRes.compilerFlags |= D3D10_SHADER_PREFER_FLOW_CONTROL; + break; + case FXC_Gis: + parserRes.compilerFlags |= D3D10_SHADER_IEEE_STRICTNESS; + break; + case FXC_Gpp: + parserRes.compilerFlags |= D3D10_SHADER_PARTIAL_PRECISION; + break; + case FXC_Op: + parserRes.compilerFlags |= D3D10_SHADER_NO_PRESHADER; + break; + case FXC_Vd: + parserRes.compilerFlags |= D3D10_SHADER_SKIP_VALIDATION; + break; + case FXC_NoLogo: + break; + default: + fprintf(stderr, "Failed assigning switch: %s\n", sw->text); + return false; + } + } + } + else if (i == numargs - 1) + { + parserRes.pInFile = args[i]; + } + else + { + fprintf(stderr, "Error in command line at token: %s\n", args[i]); + return false; + } + } + + const bool successful = parserRes.pProfile && parserRes.pEntry && parserRes.pInFile && parserRes.pOutFile; + if (!successful) + { + fprintf(stderr, "Failed to specify all required arguments: infile, outfile, profile and entry point\n"); + } + + return successful; +} + + +bool ReadInFile(const char* pInFile, std::vector& data) +{ + if (!pInFile) + { + return false; + } + + bool read = false; + + FILE* fin = 0; + fopen_s(&fin, pInFile, "rb"); + if (fin) + { + fseek(fin, 0, SEEK_END); + const long l = ftell(fin); + if (l >= 0) + { + fseek(fin, 0, SEEK_SET); + const size_t len = l > 0 ? (size_t) l : 0; + data.resize(len); + fread(&data[0], 1, len, fin); + read = true; + } + + fclose(fin); + } + + return read; +} + + +bool WriteByteCode(const char* pFileName, const void* pCode, size_t codeSize) +{ + if (!pFileName || !pCode && codeSize) + { + return false; + } + + bool written = false; + + FILE* fout = 0; + fopen_s(&fout, pFileName, "wb"); + if (fout) + { + fwrite(pCode, 1, codeSize, fout); + fclose(fout); + written = true; + } + + return written; +} + + +bool WriteHexListing(const char* pFileName, const char* pHdrVarName, const char* pDisassembly, const void* pCode, size_t codeSize) +{ + if (!pFileName || !pHdrVarName || !pDisassembly || !pCode && codeSize) + { + return false; + } + + bool written = false; + + FILE* fout = 0; + fopen_s(&fout, pFileName, "w"); + if (fout) + { + fprintf(fout, "#if 0\n%s#endif\n\n", pDisassembly); + fprintf(fout, "const BYTE g_%s[] = \n{", pHdrVarName); + + const size_t blockSize = 6; + const size_t numBlocks = codeSize / blockSize; + + const unsigned char* p = (const unsigned char*) pCode; + + size_t i = 0; + for (; i < numBlocks * blockSize; i += blockSize) + { + fprintf(fout, "\n %3d, %3d, %3d, %3d, %3d, %3d", p[i], p[i + 1], p[i + 2], p[i + 3], p[i + 4], p[i + 5]); + if (i + blockSize < codeSize) + { + fprintf(fout, ","); + } + } + + if (i < codeSize) + { + fprintf(fout, "\n "); + + for (; i < codeSize; ++i) + { + fprintf(fout, "%3d", p[i]); + if (i < codeSize - 1) + { + fprintf(fout, ", "); + } + } + } + + fprintf(fout, "\n};\n"); + + fclose(fout); + written = true; + } + + return written; +} + + +void DisplayInfo() +{ + fprintf(stdout, "FXC stub for remote shader compile server\n(C) 2012 Crytek. All rights reserved.\n\nVersion "CRYFXC_VER " for %d bit, linked against D3DCompiler_%d.dll\n\n", sizeof(void*) * 8, D3DX11_SDK_VERSION); + fprintf(stdout, "Syntax: fxc SwitchOptions Filename\n\n"); + fprintf(stdout, "Supported switches: "); + + bool firstSw = true; + for (size_t i = 0; i < sizeof(s_switchEntries) / sizeof(s_switchEntries[0]); ++i) + { + if (s_switchEntries[i].supported) + { + fprintf(stdout, "%s%s", firstSw ? "" : ", ", s_switchEntries[i].text); + firstSw = false; + } + } + + fprintf(stdout, "\n"); +} + + +int _tmain(int argc, _TCHAR* argv[]) +{ + if (argc == 1) + { + DisplayInfo(); + return 0; + } + + ParserResults parserRes; + if (!ParseCommandLine(argv, argc, parserRes)) + { + return 1; + } + + std::vector program; + if (!ReadInFile(parserRes.pInFile, program)) + { + fprintf(stderr, "Failed to read input file: %s\n", parserRes.pInFile); + return 1; + } + + ID3D10Blob* pShader = 0; + ID3D10Blob* pErr = 0; + + bool successful = SUCCEEDED(D3DCompile(&program[0], program.size(), parserRes.pInFile, 0, 0, parserRes.pEntry, parserRes.pProfile, parserRes.compilerFlags, 0, &pShader, &pErr)) && pShader; + + if (successful) + { + const unsigned char* pCode = (unsigned char*) pShader->GetBufferPointer(); + const size_t codeSize = pShader->GetBufferSize(); + + if (!parserRes.disassemble) + { + successful = WriteByteCode(parserRes.pOutFile, pCode, codeSize); + if (!successful) + { + fprintf(stderr, "Failed to write output file: %s\n", parserRes.pOutFile); + } + } + else + { + ID3D10Blob* pDisassembled = 0; + successful = SUCCEEDED(D3DDisassemble(pCode, codeSize, 0, 0, &pDisassembled)) && pDisassembled; + + if (successful) + { + const char* pDisassembly = (char*) pDisassembled->GetBufferPointer(); + const char* pHdrVarName = parserRes.pHeaderVariableName ? parserRes.pHeaderVariableName : parserRes.pEntry; + successful = WriteHexListing(parserRes.pOutFile, pHdrVarName, pDisassembly, pCode, codeSize); + if (!successful) + { + fprintf(stderr, "Failed to write output file: %s\n", parserRes.pOutFile); + } + } + else + { + fprintf(stderr, "Failed to disassemble shader code\n", parserRes.pOutFile); + } + + if (pDisassembled) + { + pDisassembled->Release(); + pDisassembled = 0; + } + } + } + else + { + if (pErr) + { + const char* pMsg = (const char*) pErr->GetBufferPointer(); + fprintf(stderr, "%s\n", pMsg); + } + } + + if (pShader) + { + pShader->Release(); + pShader = 0; + } + + if (pErr) + { + pErr->Release(); + pErr = 0; + } + + return successful ? 0 : 1; +} diff --git a/Code/Tools/CryFXC/cryfxc/cryfxc.vcxproj b/Code/Tools/CryFXC/cryfxc/cryfxc.vcxproj new file mode 100644 index 0000000000..ab57f4c7f6 --- /dev/null +++ b/Code/Tools/CryFXC/cryfxc/cryfxc.vcxproj @@ -0,0 +1,153 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A505D345-D712-4C80-8BDE-6FBC08A390D8} + Win32Proj + cryfxc + + + + Application + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + true + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + true + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/Code/Tools/CryFXC/cryfxc/stdafx.cpp b/Code/Tools/CryFXC/cryfxc/stdafx.cpp new file mode 100644 index 0000000000..209929990b --- /dev/null +++ b/Code/Tools/CryFXC/cryfxc/stdafx.cpp @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +// Original file Copyright Crytek GMBH or its affiliates, used under license. + +#include "stdafx.h" diff --git a/Code/Tools/CryFXC/cryfxc/stdafx.h b/Code/Tools/CryFXC/cryfxc/stdafx.h new file mode 100644 index 0000000000..698d14574b --- /dev/null +++ b/Code/Tools/CryFXC/cryfxc/stdafx.h @@ -0,0 +1,29 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +// Original file Copyright Crytek GMBH or its affiliates, used under license. + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include +#include +#include + +#include + +#include +#include diff --git a/Code/Tools/CryFXC/cryfxc/targetver.h b/Code/Tools/CryFXC/cryfxc/targetver.h new file mode 100644 index 0000000000..d139ba1901 --- /dev/null +++ b/Code/Tools/CryFXC/cryfxc/targetver.h @@ -0,0 +1,16 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +// Original file Copyright Crytek GMBH or its affiliates, used under license. + +#pragma once + +#include diff --git a/Code/Tools/HLSLCrossCompiler/CMakeLists.txt b/Code/Tools/HLSLCrossCompiler/CMakeLists.txt new file mode 100644 index 0000000000..3b60e715a8 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/CMakeLists.txt @@ -0,0 +1,57 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +ly_add_target( + NAME HLSLcc.Headers HEADERONLY + NAMESPACE AZ + FILES_CMAKE + hlslcc_header_files.cmake + INCLUDE_DIRECTORIES + INTERFACE + include +) + +if (NOT PAL_TRAIT_BUILD_HOST_TOOLS) + return() +endif() + +ly_add_target( + NAME HLSLcc EXECUTABLE + NAMESPACE AZ + OUTPUT_SUBDIRECTORY Compiler/PCGL/V006 + FILES_CMAKE + hlslcc_files.cmake + PLATFORM_INCLUDE_FILES + Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake + INCLUDE_DIRECTORIES + PRIVATE + src + src/cbstring + offline/cjson + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + PUBLIC + AZ::HLSLcc.Headers +) +ly_add_source_properties( + SOURCES + offline/compilerStandalone.cpp + offline/cjson/cJSON.c + src/toGLSL.c + src/toGLSLDeclaration.c + src/cbstring/bstrlib.c + src/cbstring/bstraux.c + src/reflect.c + src/amazon_changes.c + PROPERTY COMPILE_DEFINITIONS + VALUES _CRT_SECURE_NO_WARNINGS +) diff --git a/Code/Tools/HLSLCrossCompiler/Platform/Linux/platform_linux.cmake b/Code/Tools/HLSLCrossCompiler/Platform/Linux/platform_linux.cmake new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/Platform/Linux/platform_linux.cmake @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/Code/Tools/HLSLCrossCompiler/Platform/Mac/platform_mac.cmake b/Code/Tools/HLSLCrossCompiler/Platform/Mac/platform_mac.cmake new file mode 100644 index 0000000000..f5b9ea77a2 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/Platform/Mac/platform_mac.cmake @@ -0,0 +1,11 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + diff --git a/Code/Tools/HLSLCrossCompiler/Platform/Windows/platform_windows.cmake b/Code/Tools/HLSLCrossCompiler/Platform/Windows/platform_windows.cmake new file mode 100644 index 0000000000..926c831fb9 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/Platform/Windows/platform_windows.cmake @@ -0,0 +1,18 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +file(TO_CMAKE_PATH "$ENV{ProgramFiles\(x86\)}" program_files_path) + +ly_add_target_files( + TARGETS HLSLcc + FILES + "${program_files_path}/Windows Kits/10/bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64/d3dcompiler_47.dll" +) diff --git a/Code/Tools/HLSLCrossCompiler/README b/Code/Tools/HLSLCrossCompiler/README new file mode 100644 index 0000000000..4369f1a3b2 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/README @@ -0,0 +1,71 @@ +Overview: + This is a modified version of https://github.com/James-Jones/HLSLCrossCompiler + + It can be used either: + 1. As an executable. + This is the default use case for release builds + This is run by the RemoteShaderCompiler when compiling the shaders for the GL4 and GLES3 platforms. + 2. As a static library. + This is used by the DXGL translation layer if compiled with DXGL_USE_GLSL set to 0. + In this case DXGL translation layer to translate DirectX shader model 5 bytecode coming from the renderer front end (runtime translation). + +Editing: + When modifying the source code, in order to use the updated version in the engine, you will have to recompile the library. + To do this, please follow these steps: + + A. Edit /Code/Tools/HLSLCrossCompiler/bin/mk/rsc_version.txt and bump the version string. + Please use the format for released branches and main: + V[3_decimal_digits_version_number] + and optionally for development branches: + V[3_decimal_digits_version_number]_[custom_version_label] + + B. From a Windows machine: + Verify that the following folders and the contained files are writeable (checkout if needed): + - /Code/Tools/HLSLCrossCompiler/bin + - /Code/Tools/HLSLCrossCompiler/lib + - /Tools/RemoteShaderCompiler/Compiler/PCGL + Run: + /Code/Tools/HLSLCrossCompiler/mk/build_win_all.py + Note: + This will compile: + - The static library (2) for win32 and win64 in release + - The executable (1) with the PORTABLE define enabled (required to run from machines without Direct3D runtime, such ass the RSC servers) + for win64 release in and place it in /Tools/RemoteShaderCompiler/Compiler/PCGL/[rsc_version]/ + + C. From a Linux machine: + Verify that the following folder and the contained files are writeable (checkout if needed): + - /Code/Tools/HLSLCrossCompiler/lib + Run: + /Code/Tools/HLSLCrossCompiler/mk/build_linux_all.py + Note: + This will compile: + - The static library (2) for linux (64 bit) in release + - The static library (2) for android (android-armeabi-v7a) in release + + D. Edit: + /Code/CryEngine/RenderDll/Common/Shaders/ShaderCache.cpp + and update the two command lines in CShaderMan::mfGetShaderCompileFlags: + const char* pCompilerGL4="PCGL/[rsc_version]/HLSLcc.exe [generic_gl4_flags ...]"; + const char* pCompilerGLES3="PCGL/[rsc_version]/HLSLcc.exe [generic_gles3_flags ...]"; + with the rsc_version string chosen. + + E. Edit: + /Code/CryEngine/RenderDll/Common/Shaders/Shader.h + and bump by one minor decimal unit: + #define FX_CACHE_VER [major_decimal_digit_0].[minor_decimal_digit_0] + Note: + This is required to flush cached shaders generated with the previous versions that might be stored + in ShaderCache.pak or in a user cache folder. + +Submitting: + Before submitting any change to HLSLCrossCompiler source code, please + make sure to do so together with the updated: + /Code/Tools/HLSLCrossCompiler/bin/mk/rsc_version.txt + /Code/Tools/HLSLCrossCompiler/lib/win64/libHLSLcc.lib + /Code/Tools/HLSLCrossCompiler/lib/win32/libHLSLcc.lib + /Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc.a + /Code/Tools/HLSLCrossCompiler/lib/android-armeabi-v7a/libHLSLcc.a + /Code/CryEngine/RenderDll/Common/Shaders/ShaderCache.cpp + /Code/CryEngine/RenderDll/Common/Shaders/Shader.h + /Tools/RemoteShaderCompiler/Compiler/PCGL/[rsc_version]/HLSLcc.exe + This will make sure there is no mismatch between any cached shaders, and remotely or locally compiled shaders. \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompiler/hlslcc_files.cmake b/Code/Tools/HLSLCrossCompiler/hlslcc_files.cmake new file mode 100644 index 0000000000..f19b52084a --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/hlslcc_files.cmake @@ -0,0 +1,60 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + offline/hash.h + offline/serializeReflection.h + offline/timer.h + offline/compilerStandalone.cpp + offline/serializeReflection.cpp + offline/timer.cpp + offline/cjson/cJSON.h + offline/cjson/cJSON.c + src/amazon_changes.c + src/decode.c + src/decodeDX9.c + src/reflect.c + src/toGLSL.c + src/toGLSLDeclaration.c + src/toGLSLInstruction.c + src/toGLSLOperand.c + src/hlslccToolkit.c + src/internal_includes/debug.h + src/internal_includes/decode.h + src/internal_includes/hlslcc_malloc.h + src/internal_includes/hlslcc_malloc.c + src/internal_includes/languages.h + src/internal_includes/reflect.h + src/internal_includes/shaderLimits.h + src/internal_includes/structs.h + src/internal_includes/toGLSLDeclaration.h + src/internal_includes/toGLSLInstruction.h + src/internal_includes/toGLSLOperand.h + src/internal_includes/tokens.h + src/internal_includes/tokensDX9.h + src/internal_includes/hlslccToolkit.h + src/cbstring/bsafe.h + src/cbstring/bstraux.h + src/cbstring/bstrlib.h + src/cbstring/bsafe.c + src/cbstring/bstraux.c + src/cbstring/bstrlib.c + include/amazon_changes.h + include/hlslcc.h + include/hlslcc.hpp + include/hlslcc_bin.hpp + include/pstdint.h +) + +set(SKIP_UNITY_BUILD_INCLUSION_FILES + # 'bsafe.c' tries to forward declar 'strncpy', 'strncat', etc, but they are already declared in other modules. Remove from unity builds conideration + src/cbstring/bsafe.c +) \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompiler/hlslcc_header_files.cmake b/Code/Tools/HLSLCrossCompiler/hlslcc_header_files.cmake new file mode 100644 index 0000000000..f242cc95e6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/hlslcc_header_files.cmake @@ -0,0 +1,18 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES hlslcc_files.cmake + include/amazon_changes.h + include/hlslcc.h + include/hlslcc.hpp + include/pstdint.h + include/hlslcc_bin.hpp +) diff --git a/Code/Tools/HLSLCrossCompiler/include/amazon_changes.h b/Code/Tools/HLSLCrossCompiler/include/amazon_changes.h new file mode 100644 index 0000000000..bbc2b22625 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/include/amazon_changes.h @@ -0,0 +1,13 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef AMAZON_CHANGES_H +#define AMAZON_CHANGES_H + +// There is a bug on the Adreno 420 driver where reinterpret casts can destroy a variable. We need to replace all instances that look like this: +// floatBitsToInt(Temp2); +// We do not need to change cases that evaluate an expression within the cast operation, like so: +// floatBitsToInt(Temp2 + 1.0f); +void ModifyLineForQualcommReinterpretCastBug( HLSLCrossCompilerContext* psContext, bstring* originalString, bstring* overloadString ); + +#endif // AMAZON_CHANGES_H diff --git a/Code/Tools/HLSLCrossCompiler/include/hlslcc.h b/Code/Tools/HLSLCrossCompiler/include/hlslcc.h new file mode 100644 index 0000000000..efa43d8f4f --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/include/hlslcc.h @@ -0,0 +1,580 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef HLSLCC_H_ +#define HLSLCC_H_ + +#if defined (_WIN32) && defined(HLSLCC_DYNLIB) + #define HLSLCC_APIENTRY __stdcall + #if defined(libHLSLcc_EXPORTS) + #define HLSLCC_API __declspec(dllexport) + #else + #define HLSLCC_API __declspec(dllimport) + #endif +#else + #define HLSLCC_APIENTRY + #define HLSLCC_API +#endif + +#include +#include + +typedef enum +{ + LANG_DEFAULT,// Depends on the HLSL shader model. + LANG_ES_100, + LANG_ES_300, + LANG_ES_310, + LANG_120, + LANG_130, + LANG_140, + LANG_150, + LANG_330, + LANG_400, + LANG_410, + LANG_420, + LANG_430, + LANG_440, +} GLLang; + +typedef struct { + uint32_t ARB_explicit_attrib_location : 1; + uint32_t ARB_explicit_uniform_location : 1; + uint32_t ARB_shading_language_420pack : 1; +}GlExtensions; + +enum {MAX_SHADER_VEC4_OUTPUT = 512}; +enum {MAX_SHADER_VEC4_INPUT = 512}; +enum {MAX_TEXTURES = 128}; +enum {MAX_FORK_PHASES = 2}; +enum {MAX_FUNCTION_BODIES = 1024}; +enum {MAX_CLASS_TYPES = 1024}; +enum {MAX_FUNCTION_POINTERS = 128}; + +//Reflection +#define MAX_REFLECT_STRING_LENGTH 512 +#define MAX_SHADER_VARS 256 +#define MAX_CBUFFERS 256 +#define MAX_UAV 256 +#define MAX_FUNCTION_TABLES 256 +#define MAX_RESOURCE_BINDINGS 256 + +//Operands flags +#define TO_FLAG_NONE 0x0 +#define TO_FLAG_INTEGER 0x1 +#define TO_FLAG_NAME_ONLY 0x2 +#define TO_FLAG_DECLARATION_NAME 0x4 +#define TO_FLAG_DESTINATION 0x8 //Operand is being written to by assignment. +#define TO_FLAG_UNSIGNED_INTEGER 0x10 +#define TO_FLAG_DOUBLE 0x20 +#define TO_FLAG_FLOAT 0x40 +#define TO_FLAG_COPY 0x80 + +typedef enum SPECIAL_NAME +{ + NAME_UNDEFINED = 0, + NAME_POSITION = 1, + NAME_CLIP_DISTANCE = 2, + NAME_CULL_DISTANCE = 3, + NAME_RENDER_TARGET_ARRAY_INDEX = 4, + NAME_VIEWPORT_ARRAY_INDEX = 5, + NAME_VERTEX_ID = 6, + NAME_PRIMITIVE_ID = 7, + NAME_INSTANCE_ID = 8, + NAME_IS_FRONT_FACE = 9, + NAME_SAMPLE_INDEX = 10, + // The following are added for D3D11 + NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR = 11, + NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR = 12, + NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR = 13, + NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR = 14, + NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR = 15, + NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR = 16, + NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR = 17, + NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR = 18, + NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR = 19, + NAME_FINAL_TRI_INSIDE_TESSFACTOR = 20, + NAME_FINAL_LINE_DETAIL_TESSFACTOR = 21, + NAME_FINAL_LINE_DENSITY_TESSFACTOR = 22, +} SPECIAL_NAME; + + +typedef enum { + INOUT_COMPONENT_UNKNOWN = 0, + INOUT_COMPONENT_UINT32 = 1, + INOUT_COMPONENT_SINT32 = 2, + INOUT_COMPONENT_FLOAT32 = 3 +} INOUT_COMPONENT_TYPE; + +typedef enum MIN_PRECISION { + MIN_PRECISION_DEFAULT = 0, + MIN_PRECISION_FLOAT_16 = 1, + MIN_PRECISION_FLOAT_2_8 = 2, + MIN_PRECISION_RESERVED = 3, + MIN_PRECISION_SINT_16 = 4, + MIN_PRECISION_UINT_16 = 5, + MIN_PRECISION_ANY_16 = 0xf0, + MIN_PRECISION_ANY_10 = 0xf1 +} MIN_PRECISION; + +typedef struct InOutSignature_TAG +{ + char SemanticName[MAX_REFLECT_STRING_LENGTH]; + uint32_t ui32SemanticIndex; + SPECIAL_NAME eSystemValueType; + INOUT_COMPONENT_TYPE eComponentType; + uint32_t ui32Register; + uint32_t ui32Mask; + uint32_t ui32ReadWriteMask; + + uint32_t ui32Stream; + MIN_PRECISION eMinPrec; + +} InOutSignature; + +typedef enum ResourceType_TAG +{ + RTYPE_CBUFFER,//0 + RTYPE_TBUFFER,//1 + RTYPE_TEXTURE,//2 + RTYPE_SAMPLER,//3 + RTYPE_UAV_RWTYPED,//4 + RTYPE_STRUCTURED,//5 + RTYPE_UAV_RWSTRUCTURED,//6 + RTYPE_BYTEADDRESS,//7 + RTYPE_UAV_RWBYTEADDRESS,//8 + RTYPE_UAV_APPEND_STRUCTURED,//9 + RTYPE_UAV_CONSUME_STRUCTURED,//10 + RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER,//11 + RTYPE_COUNT, +} ResourceType; + +typedef enum ResourceGroup_TAG { + RGROUP_CBUFFER, + RGROUP_TEXTURE, + RGROUP_SAMPLER, + RGROUP_UAV, + RGROUP_COUNT, +} ResourceGroup; + +typedef enum REFLECT_RESOURCE_DIMENSION +{ + REFLECT_RESOURCE_DIMENSION_UNKNOWN = 0, + REFLECT_RESOURCE_DIMENSION_BUFFER = 1, + REFLECT_RESOURCE_DIMENSION_TEXTURE1D = 2, + REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY = 3, + REFLECT_RESOURCE_DIMENSION_TEXTURE2D = 4, + REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY = 5, + REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS = 6, + REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY = 7, + REFLECT_RESOURCE_DIMENSION_TEXTURE3D = 8, + REFLECT_RESOURCE_DIMENSION_TEXTURECUBE = 9, + REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY = 10, + REFLECT_RESOURCE_DIMENSION_BUFFEREX = 11, +} REFLECT_RESOURCE_DIMENSION; + +typedef struct ResourceBinding_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + ResourceType eType; + uint32_t ui32BindPoint; + uint32_t ui32BindCount; + uint32_t ui32Flags; + REFLECT_RESOURCE_DIMENSION eDimension; + uint32_t ui32ReturnType; + uint32_t ui32NumSamples; +} ResourceBinding; + +// Do not change the value of these enums or they will not match what we find in the DXBC file +typedef enum _SHADER_VARIABLE_TYPE { + SVT_VOID = 0, + SVT_BOOL = 1, + SVT_INT = 2, + SVT_FLOAT = 3, + SVT_STRING = 4, + SVT_TEXTURE = 5, + SVT_TEXTURE1D = 6, + SVT_TEXTURE2D = 7, + SVT_TEXTURE3D = 8, + SVT_TEXTURECUBE = 9, + SVT_SAMPLER = 10, + SVT_PIXELSHADER = 15, + SVT_VERTEXSHADER = 16, + SVT_UINT = 19, + SVT_UINT8 = 20, + SVT_GEOMETRYSHADER = 21, + SVT_RASTERIZER = 22, + SVT_DEPTHSTENCIL = 23, + SVT_BLEND = 24, + SVT_BUFFER = 25, + SVT_CBUFFER = 26, + SVT_TBUFFER = 27, + SVT_TEXTURE1DARRAY = 28, + SVT_TEXTURE2DARRAY = 29, + SVT_RENDERTARGETVIEW = 30, + SVT_DEPTHSTENCILVIEW = 31, + SVT_TEXTURE2DMS = 32, + SVT_TEXTURE2DMSARRAY = 33, + SVT_TEXTURECUBEARRAY = 34, + SVT_HULLSHADER = 35, + SVT_DOMAINSHADER = 36, + SVT_INTERFACE_POINTER = 37, + SVT_COMPUTESHADER = 38, + SVT_DOUBLE = 39, + SVT_RWTEXTURE1D = 40, + SVT_RWTEXTURE1DARRAY = 41, + SVT_RWTEXTURE2D = 42, + SVT_RWTEXTURE2DARRAY = 43, + SVT_RWTEXTURE3D = 44, + SVT_RWBUFFER = 45, + SVT_BYTEADDRESS_BUFFER = 46, + SVT_RWBYTEADDRESS_BUFFER = 47, + SVT_STRUCTURED_BUFFER = 48, + SVT_RWSTRUCTURED_BUFFER = 49, + SVT_APPEND_STRUCTURED_BUFFER = 50, + SVT_CONSUME_STRUCTURED_BUFFER = 51, + + // Partial precision types + SVT_FLOAT10 = 53, + SVT_FLOAT16 = 54, + SVT_INT16 = 156, + SVT_INT12 = 157, + SVT_UINT16 = 158, + + SVT_FORCE_DWORD = 0x7fffffff +} SHADER_VARIABLE_TYPE; + +typedef enum _SHADER_VARIABLE_CLASS { + SVC_SCALAR = 0, + SVC_VECTOR = ( SVC_SCALAR + 1 ), + SVC_MATRIX_ROWS = ( SVC_VECTOR + 1 ), + SVC_MATRIX_COLUMNS = ( SVC_MATRIX_ROWS + 1 ), + SVC_OBJECT = ( SVC_MATRIX_COLUMNS + 1 ), + SVC_STRUCT = ( SVC_OBJECT + 1 ), + SVC_INTERFACE_CLASS = ( SVC_STRUCT + 1 ), + SVC_INTERFACE_POINTER = ( SVC_INTERFACE_CLASS + 1 ), + SVC_FORCE_DWORD = 0x7fffffff +} SHADER_VARIABLE_CLASS; + +typedef struct ShaderVarType_TAG { + SHADER_VARIABLE_CLASS Class; + SHADER_VARIABLE_TYPE Type; + uint32_t Rows; + uint32_t Columns; + uint32_t Elements; + uint32_t MemberCount; + uint32_t Offset; + char Name[MAX_REFLECT_STRING_LENGTH]; + + uint32_t ParentCount; + struct ShaderVarType_TAG * Parent; + + struct ShaderVarType_TAG * Members; +} ShaderVarType; + +typedef struct ShaderVar_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + int haveDefaultValue; + uint32_t* pui32DefaultValues; + //Offset/Size in bytes. + uint32_t ui32StartOffset; + uint32_t ui32Size; + uint32_t ui32Flags; + + ShaderVarType sType; +} ShaderVar; + +typedef struct ConstantBuffer_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + + uint32_t ui32NumVars; + ShaderVar asVars[MAX_SHADER_VARS]; + + uint32_t ui32TotalSizeInBytes; + int blob; +} ConstantBuffer; + +typedef struct ClassType_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + uint16_t ui16ID; + uint16_t ui16ConstBufStride; + uint16_t ui16Texture; + uint16_t ui16Sampler; +} ClassType; + +typedef struct ClassInstance_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + uint16_t ui16ID; + uint16_t ui16ConstBuf; + uint16_t ui16ConstBufOffset; + uint16_t ui16Texture; + uint16_t ui16Sampler; +} ClassInstance; + +typedef enum TESSELLATOR_PARTITIONING +{ + TESSELLATOR_PARTITIONING_UNDEFINED = 0, + TESSELLATOR_PARTITIONING_INTEGER = 1, + TESSELLATOR_PARTITIONING_POW2 = 2, + TESSELLATOR_PARTITIONING_FRACTIONAL_ODD = 3, + TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN = 4 +} TESSELLATOR_PARTITIONING; + +typedef enum TESSELLATOR_OUTPUT_PRIMITIVE +{ + TESSELLATOR_OUTPUT_UNDEFINED = 0, + TESSELLATOR_OUTPUT_POINT = 1, + TESSELLATOR_OUTPUT_LINE = 2, + TESSELLATOR_OUTPUT_TRIANGLE_CW = 3, + TESSELLATOR_OUTPUT_TRIANGLE_CCW = 4 +} TESSELLATOR_OUTPUT_PRIMITIVE; + +typedef enum INTERPOLATION_MODE +{ + INTERPOLATION_UNDEFINED = 0, + INTERPOLATION_CONSTANT = 1, + INTERPOLATION_LINEAR = 2, + INTERPOLATION_LINEAR_CENTROID = 3, + INTERPOLATION_LINEAR_NOPERSPECTIVE = 4, + INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID = 5, + INTERPOLATION_LINEAR_SAMPLE = 6, + INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE = 7, +} INTERPOLATION_MODE; + +typedef enum TRACE_VARIABLE_GROUP +{ + TRACE_VARIABLE_INPUT = 0, + TRACE_VARIABLE_TEMP = 1, + TRACE_VARIABLE_OUTPUT = 2 +} TRACE_VARIABLE_GROUP; + +typedef enum TRACE_VARIABLE_TYPE +{ + TRACE_VARIABLE_FLOAT = 0, + TRACE_VARIABLE_SINT = 1, + TRACE_VARIABLE_UINT = 2, + TRACE_VARIABLE_DOUBLE = 3, + TRACE_VARIABLE_UNKNOWN = 4 +} TRACE_VARIABLE_TYPE; + +typedef struct VariableTraceInfo_TAG +{ + TRACE_VARIABLE_GROUP eGroup; + TRACE_VARIABLE_TYPE eType; + uint8_t ui8Index; + uint8_t ui8Component; +} VariableTraceInfo; + +typedef struct StepTraceInfo_TAG +{ + uint32_t ui32NumVariables; + VariableTraceInfo* psVariables; +} StepTraceInfo; + +typedef enum SYMBOL_TYPE +{ + SYMBOL_TESSELLATOR_PARTITIONING = 0, + SYMBOL_TESSELLATOR_OUTPUT_PRIMITIVE = 1, + SYMBOL_INPUT_INTERPOLATION_MODE = 2, + SYMBOL_EMULATE_DEPTH_CLAMP = 3 +} SYMBOL_TYPE; + +typedef struct Symbol_TAG +{ + SYMBOL_TYPE eType; + uint32_t ui32ID; + uint32_t ui32Value; +} Symbol; + +typedef struct EmbeddedResourceName_TAG +{ + uint32_t ui20Offset : 20; + uint32_t ui12Size : 12; +} EmbeddedResourceName; + +typedef struct SamplerMask_TAG +{ + uint32_t ui10TextureBindPoint : 10; + uint32_t ui10SamplerBindPoint : 10; + uint32_t ui10TextureUnit : 10; + uint32_t bNormalSample : 1; + uint32_t bCompareSample : 1; +} SamplerMask; + +typedef struct Sampler_TAG +{ + SamplerMask sMask; + EmbeddedResourceName sNormalName; + EmbeddedResourceName sCompareName; +} Sampler; + +typedef struct Resource_TAG +{ + uint32_t ui32BindPoint; + ResourceGroup eGroup; + EmbeddedResourceName sName; +} Resource; + +typedef struct ShaderInfo_TAG +{ + uint32_t ui32MajorVersion; + uint32_t ui32MinorVersion; + + uint32_t ui32NumInputSignatures; + InOutSignature* psInputSignatures; + + uint32_t ui32NumOutputSignatures; + InOutSignature* psOutputSignatures; + + uint32_t ui32NumResourceBindings; + ResourceBinding* psResourceBindings; + + uint32_t ui32NumConstantBuffers; + ConstantBuffer* psConstantBuffers; + ConstantBuffer* psThisPointerConstBuffer; + + uint32_t ui32NumClassTypes; + ClassType* psClassTypes; + + uint32_t ui32NumClassInstances; + ClassInstance* psClassInstances; + + //Func table ID to class name ID. + uint32_t aui32TableIDToTypeID[MAX_FUNCTION_TABLES]; + + uint32_t aui32ResourceMap[RGROUP_COUNT][MAX_RESOURCE_BINDINGS]; + + // GLSL resources + Sampler asSamplers[MAX_RESOURCE_BINDINGS]; + Resource asImages[MAX_RESOURCE_BINDINGS]; + Resource asUniformBuffers[MAX_RESOURCE_BINDINGS]; + Resource asStorageBuffers[MAX_RESOURCE_BINDINGS]; + uint32_t ui32NumSamplers; + uint32_t ui32NumImages; + uint32_t ui32NumUniformBuffers; + uint32_t ui32NumStorageBuffers; + + // Trace info if tracing is enabled + uint32_t ui32NumTraceSteps; + StepTraceInfo* psTraceSteps; + + // Symbols imported + uint32_t ui32NumImports; + Symbol* psImports; + + // Symbols exported + uint32_t ui32NumExports; + Symbol* psExports; + + // Hash of the input shader for debugging purposes + uint32_t ui32InputHash; + + // Offset in the GLSL string where symbol definitions can be inserted + uint32_t ui32SymbolsOffset; + + TESSELLATOR_PARTITIONING eTessPartitioning; + TESSELLATOR_OUTPUT_PRIMITIVE eTessOutPrim; + + //Required if PixelInterpDependency is true + INTERPOLATION_MODE aePixelInputInterpolation[MAX_SHADER_VEC4_INPUT]; +} ShaderInfo; + +typedef struct +{ + int shaderType; //One of the GL enums. + char* sourceCode; + ShaderInfo reflection; + GLLang GLSLLanguage; +} GLSLShader; + +typedef enum _FRAMEBUFFER_FETCH_TYPE +{ + FBF_NONE = 0, + FBF_EXT_COLOR = 1 << 0, + FBF_ARM_COLOR = 1 << 1, + FBF_ARM_DEPTH = 1 << 2, + FBF_ARM_STENCIL = 1 << 3, + FBF_ANY = FBF_EXT_COLOR | FBF_ARM_COLOR | FBF_ARM_DEPTH | FBF_ARM_STENCIL +} FRAMEBUFFER_FETCH_TYPE; + +// NOTE: HLSLCC flags are specified by command line when executing this cross compiler. +// If these flags change, the command line switch '-flags=XXX' must change as well. +// Open 3D Engine composes the command line in file 'dev\Code\CryEngine\RenderDll\Common\Shaders\RemoteCompiler.cpp' + +/*HLSL constant buffers are treated as default-block unform arrays by default. This is done + to support versions of GLSL which lack ARB_uniform_buffer_object functionality. + Setting this flag causes each one to have its own uniform block. + Note: Currently the nth const buffer will be named UnformBufferN. This is likey to change to the original HLSL name in the future.*/ +static const unsigned int HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT = 0x1; + +static const unsigned int HLSLCC_FLAG_ORIGIN_UPPER_LEFT = 0x2; + +static const unsigned int HLSLCC_FLAG_PIXEL_CENTER_INTEGER = 0x4; + +static const unsigned int HLSLCC_FLAG_GLOBAL_CONSTS_NEVER_IN_UBO = 0x8; + +//GS enabled? +//Affects vertex shader (i.e. need to compile vertex shader again to use with/without GS). +//This flag is needed in order for the interfaces between stages to match when GS is in use. +//PS inputs VtxGeoOutput +//GS outputs VtxGeoOutput +//Vs outputs VtxOutput if GS enabled. VtxGeoOutput otherwise. +static const unsigned int HLSLCC_FLAG_GS_ENABLED = 0x10; + +static const unsigned int HLSLCC_FLAG_TESS_ENABLED = 0x20; + +//Either use this flag or glBindFragDataLocationIndexed. +//When set the first pixel shader output is the first input to blend +//equation, the others go to the second input. +static const unsigned int HLSLCC_FLAG_DUAL_SOURCE_BLENDING = 0x40; + +//If set, shader inputs and outputs are declared with their semantic name. +static const unsigned int HLSLCC_FLAG_INOUT_SEMANTIC_NAMES = 0x80; + +static const unsigned int HLSLCC_FLAG_INVERT_CLIP_SPACE_Y = 0x100; +static const unsigned int HLSLCC_FLAG_CONVERT_CLIP_SPACE_Z = 0x200; +static const unsigned int HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS = 0x400; +static const unsigned int HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING = 0x800; +static const unsigned int HLSLCC_FLAG_TRACING_INSTRUMENTATION = 0x1000; +static const unsigned int HLSLCC_FLAG_HASH_INPUT = 0x2000; +static const unsigned int HLSLCC_FLAG_ADD_DEBUG_HEADER = 0x4000; +static const unsigned int HLSLCC_FLAG_NO_VERSION_STRING = 0x8000; + +static const unsigned int HLSLCC_FLAG_AVOID_SHADER_LOAD_STORE_EXTENSION = 0x10000; + +// If set, HLSLcc will generate GLSL code which contains syntactic workarounds for +// driver bugs found in Qualcomm devices running OpenGL ES 3.0 +static const unsigned int HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND = 0x20000; + +// If set, HLSL DX9 lower precision qualifiers (e.g half) will be transformed to DX11 style (e.g min16float) +// before compiling. Necessary to preserve precision information. If not, FXC just silently transform +// everything to full precision (e.g float32). +static const unsigned int HLSLCC_FLAG_HALF_FLOAT_TRANSFORM = 0x40000; + +#ifdef __cplusplus +extern "C" { +#endif + +HLSLCC_API void HLSLCC_APIENTRY HLSLcc_SetMemoryFunctions(void* (*malloc_override)(size_t), + void* (*calloc_override)(size_t,size_t), + void (*free_override)(void *), + void* (*realloc_override)(void*,size_t)); + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromFile(const char* filename, unsigned int flags, GLLang language, const GlExtensions *extensions, GLSLShader* result); + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromMem(const char* shader, size_t size, unsigned int flags, GLLang language, const GlExtensions *extensions, GLSLShader* result); + +HLSLCC_API const char* HLSLCC_APIENTRY GetVersionString(GLLang language); + +HLSLCC_API void HLSLCC_APIENTRY FreeGLSLShader(GLSLShader*); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Code/Tools/HLSLCrossCompiler/include/hlslcc.hpp b/Code/Tools/HLSLCrossCompiler/include/hlslcc.hpp new file mode 100644 index 0000000000..193415f277 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/include/hlslcc.hpp @@ -0,0 +1,7 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +extern "C" { +#include "hlslcc.h" +} + diff --git a/Code/Tools/HLSLCrossCompiler/include/hlslcc_bin.hpp b/Code/Tools/HLSLCrossCompiler/include/hlslcc_bin.hpp new file mode 100644 index 0000000000..f2062e58ac --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/include/hlslcc_bin.hpp @@ -0,0 +1,419 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include + +#define FOURCC(a, b, c, d) ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24 )) + +enum +{ + DXBC_BASE_ALIGNMENT = 4, + FOURCC_DXBC = FOURCC('D', 'X', 'B', 'C'), + FOURCC_RDEF = FOURCC('R', 'D', 'E', 'F'), + FOURCC_ISGN = FOURCC('I', 'S', 'G', 'N'), + FOURCC_OSGN = FOURCC('O', 'S', 'G', 'N'), + FOURCC_PCSG = FOURCC('P', 'C', 'S', 'G'), + FOURCC_SHDR = FOURCC('S', 'H', 'D', 'R'), + FOURCC_SHEX = FOURCC('S', 'H', 'E', 'X'), + FOURCC_GLSL = FOURCC('G', 'L', 'S', 'L'), + FOURCC_ISG1 = FOURCC('I', 'S', 'G', '1'), // When lower precision float/int/uint is used + FOURCC_OSG1 = FOURCC('O', 'S', 'G', '1'), // When lower precision float/int/uint is used +}; + +#undef FOURCC + +template +inline T DXBCSwapBytes(const T& kValue) +{ + return kValue; +} + +#if defined(__BIG_ENDIAN__) || SYSTEM_IS_BIG_ENDIAN + +inline uint16_t DXBCSwapBytes(const uint16_t& uValue) +{ + return + (((uValue) >> 8) & 0xFF) | + (((uValue) << 8) & 0xFF); +} + +inline uint32_t DXBCSwapBytes(const uint32_t& uValue) +{ + return + (((uValue) >> 24) & 0x000000FF) | + (((uValue) >> 8) & 0x0000FF00) | + (((uValue) << 8) & 0x00FF0000) | + (((uValue) << 24) & 0xFF000000); +} + +#endif //defined(__BIG_ENDIAN__) || SYSTEM_IS_BIG_ENDIAN + +template +struct SDXBCBufferBase +{ + Element* m_pBegin; + Element* m_pEnd; + Element* m_pIter; + + SDXBCBufferBase(Element* pBegin, Element* pEnd) + : m_pBegin(pBegin) + , m_pEnd(pEnd) + , m_pIter(pBegin) + { + } + + bool SeekRel(int32_t iOffset) + { + Element* pIterAfter(m_pIter + iOffset); + if (pIterAfter > m_pEnd) + return false; + + m_pIter = pIterAfter; + return true; + } + + bool SeekAbs(uint32_t uPosition) + { + Element* pIterAfter(m_pBegin + uPosition); + if (pIterAfter > m_pEnd) + return false; + + m_pIter = pIterAfter; + return true; + } +}; + +struct SDXBCInputBuffer : SDXBCBufferBase +{ + SDXBCInputBuffer(const uint8_t* pBegin, const uint8_t* pEnd) + : SDXBCBufferBase(pBegin, pEnd) + { + } + + bool Read(void* pElements, size_t uSize) + { + const uint8_t* pIterAfter(m_pIter + uSize); + if (pIterAfter > m_pEnd) + return false; + + memcpy(pElements, m_pIter, uSize); + + m_pIter = pIterAfter; + return true; + } +}; + +struct SDXBCOutputBuffer : SDXBCBufferBase +{ + SDXBCOutputBuffer(uint8_t* pBegin, uint8_t* pEnd) + : SDXBCBufferBase(pBegin, pEnd) + { + } + + bool Write(const void* pElements, size_t uSize) + { + uint8_t* pIterAfter(m_pIter + uSize); + if (pIterAfter > m_pEnd) + return false; + + memcpy(m_pIter, pElements, uSize); + + m_pIter = pIterAfter; + return true; + } +}; + +template +inline bool DXBCReadAs(S& kStream, External& kValue) +{ + Internal kInternal; + bool bResult(kStream.Read(&kInternal, sizeof(Internal))); + kValue = static_cast(DXBCSwapBytes(kInternal)); + return bResult; +} + +template +inline bool DXBCWriteAs(S& kStream, Internal kValue) +{ + Internal kInternal(DXBCSwapBytes(kValue)); + return kStream.Write(&kInternal, sizeof(Internal)); +} + +template bool DXBCReadUint8 (S& kStream, T& kValue) { return DXBCReadAs(kStream, kValue); } +template bool DXBCReadUint16(S& kStream, T& kValue) { return DXBCReadAs(kStream, kValue); } +template bool DXBCReadUint32(S& kStream, T& kValue) { return DXBCReadAs(kStream, kValue); } + +template bool DXBCWriteUint8 (S& kStream, uint8_t kValue) { return DXBCWriteAs(kStream, kValue); } +template bool DXBCWriteUint16(S& kStream, uint16_t kValue) { return DXBCWriteAs(kStream, kValue); } +template bool DXBCWriteUint32(S& kStream, uint32_t kValue) { return DXBCWriteAs(kStream, kValue); } + +template +bool DXBCCopy(O& kOutput, I& kInput, size_t uSize) +{ + char acBuffer[1024]; + while (uSize > 0) + { + size_t uToCopy(std::min(uSize, sizeof(acBuffer))); + if (!kInput.Read(acBuffer, uToCopy) || + !kOutput.Write(acBuffer, uToCopy)) + return false; + uSize -= uToCopy; + } + return true; +} + +enum +{ + DXBC_SIZE_POSITION = 6 * 4, + DXBC_HEADER_SIZE = 7 * 4, + DXBC_CHUNK_HEADER_SIZE = 2 * 4, + DXBC_MAX_NUM_CHUNKS_IN = 128, + DXBC_MAX_NUM_CHUNKS_OUT = 8, + DXBC_OUT_CHUNKS_INDEX_SIZE = (1 + 1 + DXBC_MAX_NUM_CHUNKS_OUT) * 4, + DXBC_OUT_FIXED_SIZE = DXBC_HEADER_SIZE + DXBC_OUT_CHUNKS_INDEX_SIZE, +}; + +enum +{ + GLSL_HEADER_SIZE = 4 * 8, // uNumSamplers, uNumImages, uNumStorageBuffers, uNumUniformBuffers, uNumImports, uNumExports, uInputHash, uSymbolsOffset + GLSL_SAMPLER_SIZE = 4 * 3, // uSamplerField, uEmbeddedNormalName, uEmbeddedCompareName + GLSL_RESOURCE_SIZE = 4 * 2, // uBindPoint, uName + GLSL_SYMBOL_SIZE = 4 * 3, // uType, uID, uValue +}; + +inline void DXBCSizeGLSLChunk(uint32_t& uGLSLChunkSize, uint32_t& uGLSLSourceSize, const GLSLShader* pShader) +{ + uint32_t uNumSymbols( + pShader->reflection.ui32NumImports + + pShader->reflection.ui32NumExports); + uint32_t uGLSLInfoSize( + DXBC_CHUNK_HEADER_SIZE + + GLSL_HEADER_SIZE + + pShader->reflection.ui32NumSamplers * GLSL_SAMPLER_SIZE + + pShader->reflection.ui32NumImages * GLSL_RESOURCE_SIZE + + pShader->reflection.ui32NumStorageBuffers * GLSL_RESOURCE_SIZE + + pShader->reflection.ui32NumUniformBuffers * GLSL_RESOURCE_SIZE + + uNumSymbols * GLSL_SYMBOL_SIZE); + uGLSLSourceSize = (uint32_t)strlen(pShader->sourceCode) + 1; + uGLSLChunkSize = uGLSLInfoSize + uGLSLSourceSize; + uGLSLChunkSize += DXBC_BASE_ALIGNMENT - 1 - (uGLSLChunkSize - 1) % DXBC_BASE_ALIGNMENT; +} + +inline uint32_t DXBCSizeOutputChunk(uint32_t uCode, uint32_t uSizeIn) +{ + uint32_t uSizeOut; + switch (uCode) + { + case FOURCC_RDEF: + case FOURCC_ISGN: + case FOURCC_OSGN: + case FOURCC_PCSG: + case FOURCC_OSG1: + case FOURCC_ISG1: + // Preserve entire chunk + uSizeOut = uSizeIn; + break; + case FOURCC_SHDR: + case FOURCC_SHEX: + // Only keep the shader version + uSizeOut = uSizeIn < 4u ? uSizeIn : 4u; + break; + default: + // Discard the chunk + uSizeOut = 0; + break; + } + + return uSizeOut + DXBC_BASE_ALIGNMENT - 1 - (uSizeOut - 1) % DXBC_BASE_ALIGNMENT; +} + +template +size_t DXBCGetCombinedSize(I& kDXBCInput, const GLSLShader* pShader) +{ + uint32_t uNumChunksIn; + if (!kDXBCInput.SeekAbs(DXBC_HEADER_SIZE) || + !DXBCReadUint32(kDXBCInput, uNumChunksIn)) + return 0; + + uint32_t auChunkOffsetsIn[DXBC_MAX_NUM_CHUNKS_IN]; + for (uint32_t uChunk = 0; uChunk < uNumChunksIn; ++uChunk) + { + if (!DXBCReadUint32(kDXBCInput, auChunkOffsetsIn[uChunk])) + return 0; + } + + uint32_t uNumChunksOut(0); + uint32_t uOutSize(DXBC_OUT_FIXED_SIZE); + for (uint32_t uChunk = 0; uChunk < uNumChunksIn && uNumChunksOut < DXBC_MAX_NUM_CHUNKS_OUT; ++uChunk) + { + uint32_t uChunkCode, uChunkSizeIn; + if (!kDXBCInput.SeekAbs(auChunkOffsetsIn[uChunk]) || + !DXBCReadUint32(kDXBCInput, uChunkCode) || + !DXBCReadUint32(kDXBCInput, uChunkSizeIn)) + return 0; + + uint32_t uChunkSizeOut(DXBCSizeOutputChunk(uChunkCode, uChunkSizeIn)); + if (uChunkSizeOut > 0) + { + uOutSize += DXBC_CHUNK_HEADER_SIZE + uChunkSizeOut; + } + } + + uint32_t uGLSLSourceSize, uGLSLChunkSize; + DXBCSizeGLSLChunk(uGLSLChunkSize, uGLSLSourceSize, pShader); + uOutSize += uGLSLChunkSize; + + return uOutSize; +} + +template +bool DXBCCombineWithGLSL(I& kInput, O& kOutput, const GLSLShader* pShader) +{ + uint32_t uNumChunksIn; + if (!DXBCCopy(kOutput, kInput, DXBC_HEADER_SIZE) || + !DXBCReadUint32(kInput, uNumChunksIn) || + uNumChunksIn > DXBC_MAX_NUM_CHUNKS_IN) + return false; + + uint32_t auChunkOffsetsIn[DXBC_MAX_NUM_CHUNKS_IN]; + for (uint32_t uChunk = 0; uChunk < uNumChunksIn; ++uChunk) + { + if (!DXBCReadUint32(kInput, auChunkOffsetsIn[uChunk])) + return false; + } + + uint32_t auZeroChunkIndex[DXBC_OUT_CHUNKS_INDEX_SIZE] = {0}; + if (!kOutput.Write(auZeroChunkIndex, DXBC_OUT_CHUNKS_INDEX_SIZE)) + return false; + + // Copy required input chunks just after the chunk index + uint32_t uOutSize(DXBC_OUT_FIXED_SIZE); + uint32_t uNumChunksOut(0); + uint32_t auChunkOffsetsOut[DXBC_MAX_NUM_CHUNKS_OUT]; + for (uint32_t uChunk = 0; uChunk < uNumChunksIn; ++uChunk) + { + uint32_t uChunkCode, uChunkSizeIn; + if (!kInput.SeekAbs(auChunkOffsetsIn[uChunk]) || + !DXBCReadUint32(kInput, uChunkCode) || + !DXBCReadUint32(kInput, uChunkSizeIn)) + return false; + + // Filter only input chunks of the specified types + uint32_t uChunkSizeOut(DXBCSizeOutputChunk(uChunkCode, uChunkSizeIn)); + if (uChunkSizeOut > 0) + { + if (uNumChunksOut >= DXBC_MAX_NUM_CHUNKS_OUT) + return false; + + if (!DXBCWriteUint32(kOutput, uChunkCode) || + !DXBCWriteUint32(kOutput, uChunkSizeOut) || + !DXBCCopy(kOutput, kInput, uChunkSizeOut)) + return false; + + auChunkOffsetsOut[uNumChunksOut] = uOutSize; + ++uNumChunksOut; + uOutSize += DXBC_CHUNK_HEADER_SIZE + uChunkSizeOut; + } + } + + // Write GLSL chunk + uint32_t uGLSLChunkOffset(uOutSize); + uint32_t uGLSLChunkSize, uGLSLSourceSize; + DXBCSizeGLSLChunk(uGLSLChunkSize, uGLSLSourceSize, pShader); + if (!DXBCWriteUint32(kOutput, (uint32_t)FOURCC_GLSL) || + !DXBCWriteUint32(kOutput, uGLSLChunkSize) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumSamplers) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumImages) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumStorageBuffers) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumUniformBuffers) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumImports) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumExports) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32InputHash) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32SymbolsOffset)) + return false; + for (uint32_t uSampler = 0; uSampler < pShader->reflection.ui32NumSamplers; ++uSampler) + { + uint32_t uSamplerField = + (pShader->reflection.asSamplers[uSampler].sMask.ui10TextureBindPoint << 22) | + (pShader->reflection.asSamplers[uSampler].sMask.ui10SamplerBindPoint << 12) | + (pShader->reflection.asSamplers[uSampler].sMask.ui10TextureUnit << 2) | + (pShader->reflection.asSamplers[uSampler].sMask.bNormalSample << 1) | + (pShader->reflection.asSamplers[uSampler].sMask.bCompareSample << 0); + if (!DXBCWriteUint32(kOutput, uSamplerField)) + return false; + + uint32_t uEmbeddedNormalName = + (pShader->reflection.asSamplers[uSampler].sNormalName.ui20Offset << 12) | + (pShader->reflection.asSamplers[uSampler].sNormalName.ui12Size << 0); + if (!DXBCWriteUint32(kOutput, uEmbeddedNormalName)) + return false; + + uint32_t uEmbeddedCompareName = + (pShader->reflection.asSamplers[uSampler].sCompareName.ui20Offset << 12) | + (pShader->reflection.asSamplers[uSampler].sCompareName.ui12Size << 0); + if (!DXBCWriteUint32(kOutput, uEmbeddedCompareName)) + return false; + } + for (uint32_t uImage = 0; uImage < pShader->reflection.ui32NumImages; ++uImage) + { + const Resource* psResource = pShader->reflection.asImages + uImage; + uint32_t uEmbeddedName = + (psResource->sName.ui20Offset << 12) | + (psResource->sName.ui12Size << 0); + if (!DXBCWriteUint32(kOutput, psResource->ui32BindPoint) || + !DXBCWriteUint32(kOutput, uEmbeddedName)) + return false; + } + for (uint32_t uStorageBuffer = 0; uStorageBuffer < pShader->reflection.ui32NumStorageBuffers; ++uStorageBuffer) + { + const Resource* psResource = pShader->reflection.asStorageBuffers + uStorageBuffer; + uint32_t uEmbeddedName = + (psResource->sName.ui20Offset << 12) | + (psResource->sName.ui12Size << 0); + if (!DXBCWriteUint32(kOutput, psResource->ui32BindPoint) || + !DXBCWriteUint32(kOutput, uEmbeddedName)) + return false; + } + for (uint32_t uUniformBuffer = 0; uUniformBuffer < pShader->reflection.ui32NumUniformBuffers; ++uUniformBuffer) + { + const Resource* psResource = pShader->reflection.asUniformBuffers + uUniformBuffer; + uint32_t uEmbeddedName = + (psResource->sName.ui20Offset << 12) | + (psResource->sName.ui12Size << 0); + if (!DXBCWriteUint32(kOutput, psResource->ui32BindPoint) || + !DXBCWriteUint32(kOutput, uEmbeddedName)) + return false; + } + for (uint32_t uSymbol = 0; uSymbol < pShader->reflection.ui32NumImports; ++uSymbol) + { + if (!DXBCWriteUint32(kOutput, pShader->reflection.psImports[uSymbol].eType) || + !DXBCWriteUint32(kOutput, pShader->reflection.psImports[uSymbol].ui32ID) || + !DXBCWriteUint32(kOutput, pShader->reflection.psImports[uSymbol].ui32Value)) + return false; + } + for (uint32_t uSymbol = 0; uSymbol < pShader->reflection.ui32NumExports; ++uSymbol) + { + if (!DXBCWriteUint32(kOutput, pShader->reflection.psExports[uSymbol].eType) || + !DXBCWriteUint32(kOutput, pShader->reflection.psExports[uSymbol].ui32ID) || + !DXBCWriteUint32(kOutput, pShader->reflection.psExports[uSymbol].ui32Value)) + return false; + } + if (!kOutput.Write(pShader->sourceCode, uGLSLSourceSize)) + return false; + uOutSize += uGLSLChunkSize; + + // Write total size and chunk index + if (!kOutput.SeekAbs(DXBC_SIZE_POSITION) || + !DXBCWriteUint32(kOutput, uOutSize) || + !kOutput.SeekAbs(DXBC_HEADER_SIZE) || + !DXBCWriteUint32(kOutput, uNumChunksOut + 1)) + return false; + for (uint32_t uChunk = 0; uChunk < uNumChunksOut; ++uChunk) + { + if (!DXBCWriteUint32(kOutput, auChunkOffsetsOut[uChunk])) + return false; + } + DXBCWriteUint32(kOutput, uGLSLChunkOffset); + + return true; +} diff --git a/Code/Tools/HLSLCrossCompiler/include/pstdint.h b/Code/Tools/HLSLCrossCompiler/include/pstdint.h new file mode 100644 index 0000000000..6998242aa1 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/include/pstdint.h @@ -0,0 +1,801 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2011 Paul Hsieh + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + **************************************************************************** + * + * Version 0.1.12 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#ifndef uint8_t +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#ifndef int8_t +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#ifndef uint16_t +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#ifndef int16_t +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#ifndef uint32_t +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#ifndef int32_t +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif + +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are not + * defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) + +int main () { + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/jni/Android.mk b/Code/Tools/HLSLCrossCompiler/jni/Android.mk new file mode 100644 index 0000000000..66e2bb4ecf --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/jni/Android.mk @@ -0,0 +1,32 @@ +# +# Android Makefile conversion +# +# Leander Beernaert +# +# How to build: $ANDROID_NDK/ndk-build +# +VERSION=1.17 + +LOCAL_PATH := $(call my-dir)/../ + +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm +LOCAL_ARM_NEON := true + +LOCAL_MODULE := HLSLcc + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/src/cbstring +LOCAL_CFLAGS += -Wall -W +# For dynamic library +#LOCAL_CFLAGS += -DHLSLCC_DYNLIB +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/src/*.c) \ + $(wildcard $(LOCAL_PATH)/src/cbstring/*.c) \ + $(wildcard $(LOCAL_PATH)/src/internal_includes/*.c) +#LOCAL_LDLIBS += -lGLESv3 + +include $(BUILD_STATIC_LIBRARY) + diff --git a/Code/Tools/HLSLCrossCompiler/jni/Application.mk b/Code/Tools/HLSLCrossCompiler/jni/Application.mk new file mode 100644 index 0000000000..a8ae0839b1 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/jni/Application.mk @@ -0,0 +1,3 @@ +APP_PLATFORM := android-18 +APP_ABI := armeabi-v7a +APP_OPTIM := release diff --git a/Code/Tools/HLSLCrossCompiler/lib/android-armeabi-v7a/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/android-armeabi-v7a/libHLSLcc.a new file mode 100644 index 0000000000..6bab978a58 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/android-armeabi-v7a/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35c73c9602dbd539ddd4874c4231fe21d40e0db813394f89e1c837a59d4be755 +size 1092754 diff --git a/Code/Tools/HLSLCrossCompiler/lib/ios-arm64/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/ios-arm64/libHLSLcc.a new file mode 100644 index 0000000000..4e5a152c7c --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/ios-arm64/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51ed960398777ebee83d838e344e4a1dd331acb4ae0e77cbf8a64f2c1146b2ce +size 184304 diff --git a/Code/Tools/HLSLCrossCompiler/lib/ios-simx86_64/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/ios-simx86_64/libHLSLcc.a new file mode 100644 index 0000000000..cb80d6e7ee --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/ios-simx86_64/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26083d66db7a82295514575af1160ab7aec52aa32f8431edbd1a09011154901b +size 190552 diff --git a/Code/Tools/HLSLCrossCompiler/lib/ios/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/ios/libHLSLcc.a new file mode 100644 index 0000000000..c9ef9a0047 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/ios/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b322870fdff43b12034b4d9bcf72b59a5ef2f0cdd1f3042369c9f1a6911931b +size 374904 diff --git a/Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc.a new file mode 100644 index 0000000000..2adc6a7397 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ea9be963e0674546c2e8af2fd9a34e95100d9a1806399457b1e06d033149456 +size 375378 diff --git a/Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc_d.a b/Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc_d.a new file mode 100644 index 0000000000..b1318b6000 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/linux/libHLSLcc_d.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:544de0a5688c776e28b42bb189a738bc87743828b737d4bf653e46cd2e05938b +size 1171448 diff --git a/Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc.a new file mode 100644 index 0000000000..85bf31eed4 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af9216c54d23dd3754f7ae18d56b97ae256eb29a0046d8e0d2a0716054d8c230 +size 218888 diff --git a/Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc_d.a b/Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc_d.a new file mode 100644 index 0000000000..00095a3615 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/mac/libHLSLcc_d.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a07bec349614cdd3e40c3577bddace1203148016f9276c7ef807bdbc37dcabf +size 671232 diff --git a/Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc.a b/Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc.a new file mode 100644 index 0000000000..c7b92fcc1e --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88acec4cedad5699900ec2d1a3ce83ab5e9365ebea4b4af0ababba562382f399 +size 296852 diff --git a/Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc_d.a b/Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc_d.a new file mode 100644 index 0000000000..29dd7fbf7a --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/steamos/libHLSLcc_d.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c0625b7f534df5817646dd1335f9d7916389f27a83b7d118fadab504064d910 +size 1144250 diff --git a/Code/Tools/HLSLCrossCompiler/lib/win32/libHLSLcc.lib b/Code/Tools/HLSLCrossCompiler/lib/win32/libHLSLcc.lib new file mode 100644 index 0000000000..8ed661eb15 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/win32/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d49f4f011fe2835d5aafa7ac77fb660cf12078763ddef25e935da919bc65e6b +size 440242 diff --git a/Code/Tools/HLSLCrossCompiler/lib/win64/libHLSLcc.lib b/Code/Tools/HLSLCrossCompiler/lib/win64/libHLSLcc.lib new file mode 100644 index 0000000000..452aa95688 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/lib/win64/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a4a291f8b3d00e1865a98ad3c740d28ff10d43ca87b718403cfc920df2be9ab +size 618776 diff --git a/Code/Tools/HLSLCrossCompiler/license.txt b/Code/Tools/HLSLCrossCompiler/license.txt new file mode 100644 index 0000000000..29f302da75 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/license.txt @@ -0,0 +1,53 @@ +Copyright (c) 2012 James Jones +Further improvements Copyright (c) 2014-2016 Unity Technologies +All Rights Reserved. + +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. + +This software makes use of the bstring library which is provided under the following license: + +Copyright (c) 2002-2008 Paul Hsieh +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 bstrlib 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 OWNER 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. + diff --git a/Code/Tools/HLSLCrossCompiler/offline/cjson/README b/Code/Tools/HLSLCrossCompiler/offline/cjson/README new file mode 100644 index 0000000000..7531c049a6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/cjson/README @@ -0,0 +1,247 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ + +Welcome to cJSON. + +cJSON aims to be the dumbest possible parser that you can get your job done with. +It's a single file of C, and a single header file. + +JSON is described best here: http://www.json.org/ +It's like XML, but fat-free. You use it to move data around, store things, or just +generally represent your program's state. + + +First up, how do I build? +Add cJSON.c to your project, and put cJSON.h somewhere in the header search path. +For example, to build the test app: + +gcc cJSON.c test.c -o test -lm +./test + + +As a library, cJSON exists to take away as much legwork as it can, but not get in your way. +As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it +in one of two modes: Auto and Manual. Let's have a quick run-through. + + +I lifted some JSON from this page: http://www.json.org/fatfree.html +That page inspired me to write cJSON, which is a parser that tries to share the same +philosophy as JSON itself. Simple, dumb, out of the way. + +Some JSON: +{ + "name": "Jack (\"Bee\") Nimble", + "format": { + "type": "rect", + "width": 1920, + "height": 1080, + "interlace": false, + "frame rate": 24 + } +} + +Assume that you got this from a file, a webserver, or magic JSON elves, whatever, +you have a char * to it. Everything is a cJSON struct. +Get it parsed: + cJSON *root = cJSON_Parse(my_json_string); + +This is an object. We're in C. We don't have objects. But we do have structs. +What's the framerate? + + cJSON *format = cJSON_GetObjectItem(root,"format"); + int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint; + + +Want to change the framerate? + cJSON_GetObjectItem(format,"frame rate")->valueint=25; + +Back to disk? + char *rendered=cJSON_Print(root); + +Finished? Delete the root (this takes care of everything else). + cJSON_Delete(root); + +That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers +before you dereference them. If you want to see how you'd build this struct in code? + cJSON *root,*fmt; + root=cJSON_CreateObject(); + cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); + cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject()); + cJSON_AddStringToObject(fmt,"type", "rect"); + cJSON_AddNumberToObject(fmt,"width", 1920); + cJSON_AddNumberToObject(fmt,"height", 1080); + cJSON_AddFalseToObject (fmt,"interlace"); + cJSON_AddNumberToObject(fmt,"frame rate", 24); + +Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup. +Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and +a few from elsewhere. + +What about manual mode? First up you need some detail. +Let's cover how the cJSON objects represent the JSON data. +cJSON doesn't distinguish arrays from objects in handling; just type. +Each cJSON has, potentially, a child, siblings, value, a name. + +The root object has: Object Type and a Child +The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: +Sibling has type Object, name "format", and a child. +That child has type String, name "type", value "rect", and a sibling: +Sibling has type Number, name "width", value 1920, and a sibling: +Sibling has type Number, name "height", value 1080, and a sibling: +Sibling hs type False, name "interlace", and a sibling: +Sibling has type Number, name "frame rate", value 24 + +Here's the structure: +typedef struct cJSON { + struct cJSON *next,*prev; + struct cJSON *child; + + int type; + + char *valuestring; + int valueint; + double valuedouble; + + char *string; +} cJSON; + +By default all values are 0 unless set by virtue of being meaningful. + +next/prev is a doubly linked list of siblings. next takes you to your sibling, +prev takes you back from your sibling to you. +Only objects and arrays have a "child", and it's the head of the doubly linked list. +A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0. +The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in +cJSON.h + +A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read +valuedouble. + +Any entry which is in the linked list which is the child of an object will have a "string" +which is the "name" of the entry. When I said "name" in the above example, that's "string". +"string" is the JSON name for the 'variable name' if you will. + +Now you can trivially walk the lists, recursively, and parse as you please. +You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take +the root object, and traverse the structure (which is, formally, an N-tree), +and tokenise as you please. If you wanted to build a callback style parser, this is how +you'd do it (just an example, since these things are very specific): + +void parse_and_callback(cJSON *item,const char *prefix) +{ + while (item) + { + char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2); + sprintf(newprefix,"%s/%s",prefix,item->name); + int dorecurse=callback(newprefix, item->type, item); + if (item->child && dorecurse) parse_and_callback(item->child,newprefix); + item=item->next; + free(newprefix); + } +} + +The prefix process will build you a separated list, to simplify your callback handling. +The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or +let you invoke it per-item. For the item above, your callback might look like this: + +int callback(const char *name,int type,cJSON *item) +{ + if (!strcmp(name,"name")) { /* populate name */ } + else if (!strcmp(name,"format/type") { /* handle "rect" */ } + else if (!strcmp(name,"format/width") { /* 800 */ } + else if (!strcmp(name,"format/height") { /* 600 */ } + else if (!strcmp(name,"format/interlace") { /* false */ } + else if (!strcmp(name,"format/frame rate") { /* 24 */ } + return 1; +} + +Alternatively, you might like to parse iteratively. +You'd use: + +void parse_object(cJSON *item) +{ + int i; for (i=0;ichild; + while (subitem) + { + // handle subitem + if (subitem->child) parse_object(subitem->child); + + subitem=subitem->next; + } +} + +Of course, this should look familiar, since this is just a stripped-down version +of the callback-parser. + +This should cover most uses you'll find for parsing. The rest should be possible +to infer.. and if in doubt, read the source! There's not a lot of it! ;) + + +In terms of constructing JSON data, the example code above is the right way to do it. +You can, of course, hand your sub-objects to other functions to populate. +Also, if you find a use for it, you can manually build the objects. +For instance, suppose you wanted to build an array of objects? + +cJSON *objects[24]; + +cJSON *Create_array_of_anything(cJSON **items,int num) +{ + int i;cJSON *prev, *root=cJSON_CreateArray(); + for (i=0;i<24;i++) + { + if (!i) root->child=objects[i]; + else prev->next=objects[i], objects[i]->prev=prev; + prev=objects[i]; + } + return root; +} + +and simply: Create_array_of_anything(objects,24); + +cJSON doesn't make any assumptions about what order you create things in. +You can attach the objects, as above, and later add children to each +of those objects. + +As soon as you call cJSON_Print, it renders the structure to text. + + + +The test.c code shows how to handle a bunch of typical cases. If you uncomment +the code, it'll load, parse and print a bunch of test files, also from json.org, +which are more complex than I'd care to try and stash into a const char array[]. + + +Enjoy cJSON! + + +- Dave Gamble, Aug 2009 diff --git a/Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.c b/Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.c new file mode 100644 index 0000000000..78b1634fbf --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.c @@ -0,0 +1,578 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ +// Modifications copyright Amazon.com, Inc. or its affiliates. + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" +#include + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +AZ_PUSH_DISABLE_WARNING(4232, "-Wunknown-warning-option") // address of malloc/free are not static +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; +AZ_POP_DISABLE_WARNING + +static char* cJSON_strdup(const char* str) +{ + size_t len = strlen(str) + 1; + char* copy = (char*)cJSON_malloc(len); + + if (!copy) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + /* Could use sscanf for this? */ + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + sscanf(ptr+3,"%4x",&uc2);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str; + token = *ptr; + while (token && ++len) + { + if (strchr("\"\\\b\f\n\r\t",token)) len++; + else if (token<32) len+=5; + ptr++; + token = *ptr; + } + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item = cJSON_New_Item(); + if (!new_item) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=(int)strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON* new_item = cJSON_New_Item(); + if (!new_item) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + out=(char*)cJSON_malloc(fmt?depth+3:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=(int)(strlen(ret)+strlen(str))+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} diff --git a/Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.h b/Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.h new file mode 100644 index 0000000000..50ae02b6f9 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/cjson/cJSON.h @@ -0,0 +1,142 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ +// Modifications copyright Amazon.com, Inc. or its affiliates + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/offline/compilerStandalone.cpp b/Code/Tools/HLSLCrossCompiler/offline/compilerStandalone.cpp new file mode 100644 index 0000000000..5a22aa553f --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/compilerStandalone.cpp @@ -0,0 +1,803 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include +#include "hlslcc.hpp" +#include "stdlib.h" +#include "stdio.h" +#include +#include +#include "hash.h" +#include "serializeReflection.h" +#include "hlslcc_bin.hpp" + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "timer.h" + +#if defined(_WIN32) && !defined(PORTABLE) +//#define VALIDATE_OUTPUT // NOTE: THIS IS OK DURING HLSLcc DEV BUT SHOULD NOT BE USED IN PRODUCTION. SOME EXT USED ARE NO SUPPORTED ON WINDOWS. +#endif + +#if defined(VALIDATE_OUTPUT) +#if defined(_WIN32) +#include +#include + + #pragma comment(lib, "opengl32.lib") + +typedef char GLcharARB; /* native character */ +typedef unsigned int GLhandleARB; /* shader object handle */ +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +typedef void (WINAPI * PFNGLDELETEOBJECTARBPROC)(GLhandleARB obj); +typedef GLhandleARB (WINAPI * PFNGLCREATESHADEROBJECTARBPROC)(GLenum shaderType); +typedef void (WINAPI * PFNGLSHADERSOURCEARBPROC)(GLhandleARB shaderObj, GLsizei count, const GLcharARB** string, const GLint* length); +typedef void (WINAPI * PFNGLCOMPILESHADERARBPROC)(GLhandleARB shaderObj); +typedef void (WINAPI * PFNGLGETINFOLOGARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei* length, GLcharARB* infoLog); +typedef void (WINAPI * PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB obj, GLenum pname, GLint* params); +typedef GLhandleARB (WINAPI * PFNGLCREATEPROGRAMOBJECTARBPROC)(void); +typedef void (WINAPI * PFNGLATTACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB obj); +typedef void (WINAPI * PFNGLLINKPROGRAMARBPROC)(GLhandleARB programObj); +typedef void (WINAPI * PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB programObj); +typedef void (WINAPI * PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei* length, GLcharARB* infoLog); + +static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +static PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +static PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +static PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +static PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +static PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +static PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +static PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; + +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 + +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 + +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int* attribList); +static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + +void InitOpenGL() +{ + HGLRC rc; + + // setup minimal required GL + HWND wnd = CreateWindowA( + "STATIC", + "GL", + WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 16, 16, + NULL, NULL, + GetModuleHandle(NULL), NULL); + HDC dc = GetDC(wnd); + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL, + PFD_TYPE_RGBA, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 16, 0, + 0, PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int fmt = ChoosePixelFormat(dc, &pfd); + SetPixelFormat(dc, fmt, &pfd); + + rc = wglCreateContext(dc); + wglMakeCurrent(dc, rc); + + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); + + if (wglCreateContextAttribsARB) + { + const int OpenGLContextAttribs [] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, + #if defined(_DEBUG) + //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, + #else + //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + #endif + //WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, 0 + }; + + const HGLRC OpenGLContext = wglCreateContextAttribsARB(dc, 0, OpenGLContextAttribs); + + wglMakeCurrent(dc, OpenGLContext); + + wglDeleteContext(rc); + + rc = OpenGLContext; + } + + glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)wglGetProcAddress("glDeleteObjectARB"); + glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)wglGetProcAddress("glCreateShaderObjectARB"); + glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)wglGetProcAddress("glShaderSourceARB"); + glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)wglGetProcAddress("glCompileShaderARB"); + glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)wglGetProcAddress("glGetInfoLogARB"); + glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB"); + glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)wglGetProcAddress("glCreateProgramObjectARB"); + glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)wglGetProcAddress("glAttachObjectARB"); + glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)wglGetProcAddress("glLinkProgramARB"); + glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)wglGetProcAddress("glUseProgramObjectARB"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); +} +#endif + +void PrintSingleLineError(FILE* pFile, const char* error) +{ + while (*error != '\0') + { + const char* pLineEnd = strchr(error, '\n'); + if (pLineEnd == 0) + { + pLineEnd = error + strlen(error) - 1; + } + fwrite(error, 1, pLineEnd - error, pFile); + fwrite("\r", 1, 1, pFile); + error = pLineEnd + 1; + } +} + +int TryCompileShader(GLenum eShaderType, const char* inFilename, const char* shader, double* pCompileTime, int useStdErr) +{ + GLint iCompileStatus; + GLuint hShader; + Timer_t timer; + + InitTimer(&timer); + + InitOpenGL(); + + hShader = glCreateShaderObjectARB(eShaderType); + glShaderSourceARB(hShader, 1, (const char**)&shader, NULL); + + ResetTimer(&timer); + glCompileShaderARB(hShader); + *pCompileTime = ReadTimer(&timer); + + /* Check it compiled OK */ + glGetObjectParameterivARB (hShader, GL_OBJECT_COMPILE_STATUS_ARB, &iCompileStatus); + + if (iCompileStatus != GL_TRUE) + { + FILE* errorFile = NULL; + GLint iInfoLogLength = 0; + char* pszInfoLog; + + glGetObjectParameterivARB (hShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iInfoLogLength); + + pszInfoLog = new char[iInfoLogLength]; + + printf("Error: Failed to compile GLSL shader\n"); + + glGetInfoLogARB (hShader, iInfoLogLength, NULL, pszInfoLog); + + printf(pszInfoLog); + + if (!useStdErr) + { + std::string filename; + filename += inFilename; + filename += "_compileErrors.txt"; + + //Dump to file + fopen_s(&errorFile, filename.c_str(), "w"); + + fclose(errorFile); + } + else + { + // Present error to stderror with no "new lines" as required by remote shader compiler + fprintf(stderr, "%s(-) error: ", inFilename); + PrintSingleLineError(stderr, pszInfoLog); + fprintf(stderr, "\rshader: "); + PrintSingleLineError(stderr, shader); + } + + delete [] pszInfoLog; + + return 0; + } + + return 1; +} +#endif + +int fileExists(const char* path) +{ + FILE* shaderFile; + shaderFile = fopen(path, "rb"); + + if (shaderFile) + { + fclose(shaderFile); + return 1; + } + return 0; +} + +GLLang LanguageFromString(const char* str) +{ + if (strcmp(str, "es100") == 0) + { + return LANG_ES_100; + } + if (strcmp(str, "es300") == 0) + { + return LANG_ES_300; + } + if (strcmp(str, "es310") == 0) + { + return LANG_ES_310; + } + if (strcmp(str, "120") == 0) + { + return LANG_120; + } + if (strcmp(str, "130") == 0) + { + return LANG_130; + } + if (strcmp(str, "140") == 0) + { + return LANG_140; + } + if (strcmp(str, "150") == 0) + { + return LANG_150; + } + if (strcmp(str, "330") == 0) + { + return LANG_330; + } + if (strcmp(str, "400") == 0) + { + return LANG_400; + } + if (strcmp(str, "410") == 0) + { + return LANG_410; + } + if (strcmp(str, "420") == 0) + { + return LANG_420; + } + if (strcmp(str, "430") == 0) + { + return LANG_430; + } + if (strcmp(str, "440") == 0) + { + return LANG_440; + } + return LANG_DEFAULT; +} + +#define MAX_PATH_CHARS 256 +#define MAX_FXC_CMD_CHARS 1024 + +typedef struct +{ + GLLang language; + + int flags; + + const char* shaderFile; + char* outputShaderFile; + + char* reflectPath; + + char cacheKey[MAX_PATH_CHARS]; + + int bUseFxc; + std::string fxcCmdLine; +} Options; + +void InitOptions(Options* psOptions) +{ + psOptions->language = LANG_DEFAULT; + psOptions->flags = 0; + psOptions->reflectPath = NULL; + + psOptions->shaderFile = NULL; + + psOptions->bUseFxc = 0; +} + +void PrintHelp() +{ + printf("Command line options:\n"); + + printf("\t-lang=X \t GLSL language to use. e.g. es100 or 140 or metal.\n"); + printf("\t-flags=X \t The integer value of the HLSLCC_FLAGS to used.\n"); + printf("\t-reflect=X \t File to write reflection JSON to.\n"); + printf("\t-in=X \t Shader file to compile.\n"); + printf("\t-out=X \t File to write the compiled shader from -in to.\n"); + + printf("\t-hashout=[dir/]out-file-name \t Output file name is a hash of 'out-file-name', put in the directory 'dir'.\n"); + + printf("\t-fxc=\"CMD\" HLSL compiler command line. If specified the input shader will be first compiled through this command first and then the resulting bytecode translated.\n"); + + printf("\n"); +} + +int GetOptions(int argc, char** argv, Options* psOptions) +{ + int i; + int fullShaderChain = -1; + + InitOptions(psOptions); + + for (i = 1; i < argc; i++) + { + char* option; + + option = strstr(argv[i], "-help"); + if (option != NULL) + { + PrintHelp(); + return 0; + } + + option = strstr(argv[i], "-reflect="); + if (option != NULL) + { + psOptions->reflectPath = option + strlen("-reflect="); + } + + option = strstr(argv[i], "-lang="); + if (option != NULL) + { + psOptions->language = LanguageFromString((&option[strlen("-lang=")])); + } + + option = strstr(argv[i], "-flags="); + if (option != NULL) + { + psOptions->flags = atol(&option[strlen("-flags=")]); + } + + option = strstr(argv[i], "-in="); + if (option != NULL) + { + fullShaderChain = 0; + psOptions->shaderFile = option + strlen("-in="); + if (!fileExists(psOptions->shaderFile)) + { + printf("Invalid path: %s\n", psOptions->shaderFile); + return 0; + } + } + + option = strstr(argv[i], "-out="); + if (option != NULL) + { + fullShaderChain = 0; + psOptions->outputShaderFile = option + strlen("-out="); + } + + option = strstr(argv[i], "-hashout"); + if (option != NULL) + { + fullShaderChain = 0; + psOptions->outputShaderFile = option + strlen("-hashout="); + + char* dir; + int64_t length; + + uint64_t hash = hash64((const uint8_t*)psOptions->outputShaderFile, (uint32_t)strlen(psOptions->outputShaderFile), 0); + + + dir = strrchr(psOptions->outputShaderFile, '\\'); + + if (!dir) + { + dir = strrchr(psOptions->outputShaderFile, '//'); + } + + if (!dir) + { + length = 0; + } + else + { + length = (int)(dir - psOptions->outputShaderFile) + 1; + } + + for (i = 0; i < length; ++i) + { + psOptions->cacheKey[i] = psOptions->outputShaderFile[i]; + } + + //sprintf(psOptions->cacheKey, "%x%x", high, low); + sprintf(&psOptions->cacheKey[i], "%010" PRIX64, hash); + + psOptions->outputShaderFile = psOptions->cacheKey; + } + + option = strstr(argv[i], "-fxc="); + if (option != NULL) + { + char* cmdLine = option + strlen("-fxc="); + size_t cmdLineLen = strlen(cmdLine); + if (cmdLineLen == 0 || cmdLineLen + 1 >= MAX_FXC_CMD_CHARS) + { + return 0; + } + psOptions->fxcCmdLine = std::string(cmdLine, cmdLineLen); + psOptions->bUseFxc = 1; + } + } + + return 1; +} + +void* malloc_hook(size_t size) +{ + return malloc(size); +} +void* calloc_hook(size_t num, size_t size) +{ + return calloc(num, size); +} +void* realloc_hook(void* p, size_t size) +{ + return realloc(p, size); +} +void free_hook(void* p) +{ + free(p); +} + +int Run(const char* srcPath, const char* destPath, GLLang language, int flags, const char* reflectPath, GLSLShader* shader, int useStdErr) +{ + FILE* outputFile; + GLSLShader tempShader; + GLSLShader* result = shader ? shader : &tempShader; + Timer_t timer; + int compiledOK = 0; + double crossCompileTime = 0; + + HLSLcc_SetMemoryFunctions(malloc_hook, calloc_hook, free_hook, realloc_hook); + + InitTimer(&timer); + + ResetTimer(&timer); + GlExtensions ext; + ext.ARB_explicit_attrib_location = 0; + ext.ARB_explicit_uniform_location = 0; + ext.ARB_shading_language_420pack = 0; + compiledOK = TranslateHLSLFromFile(srcPath, flags, language, &ext, result); + + crossCompileTime = ReadTimer(&timer); + + if (compiledOK) + { + printf("cc time: %.2f us\n", crossCompileTime); + + if (destPath) + { + //Dump to file + outputFile = fopen(destPath, "w"); + fprintf(outputFile, result->sourceCode); + fclose(outputFile); + } + + if (reflectPath) + { + const char* jsonString = SerializeReflection(&result->reflection); + outputFile = fopen(reflectPath, "w"); + fprintf(outputFile, jsonString); + fclose(outputFile); + } + +#if defined(VALIDATE_OUTPUT) + std::string shaderSource; + if (flags & HLSLCC_FLAG_NO_VERSION_STRING) + { + // Need to add the version string so that the shader will compile + shaderSource = GetVersionString(language); + shaderSource += result->sourceCode; + } + else + { + shaderSource = result->sourceCode; + } + compiledOK = TryCompileShader(result->shaderType, destPath ? destPath : "", shaderSource.c_str(), &glslCompileTime, useStdErr); + + if (compiledOK) + { + printf("glsl time: %.2f us\n", glslCompileTime); + } +#endif + + if (!shader) + { + FreeGLSLShader(result); + } + } + else if (useStdErr) + { + fprintf(stderr, "TranslateHLSLFromFile failed"); + } + + return compiledOK; +} + +struct SDXBCFile +{ + FILE* m_pFile; + + bool Read(void* pElements, size_t uSize) + { + return fread(pElements, 1, uSize, m_pFile) == uSize; + } + + bool Write(const void* pElements, size_t uSize) + { + return fwrite(pElements, 1, uSize, m_pFile) == uSize; + } + + bool SeekRel(int32_t iOffset) + { + return fseek(m_pFile, iOffset, SEEK_CUR) == 0; + } + + bool SeekAbs(uint32_t uPosition) + { + return fseek(m_pFile, uPosition, SEEK_SET) == 0; + } +}; + +int CombineDXBCWithGLSL(char* dxbcFileName, char* outputFileName, GLSLShader* shader) +{ + SDXBCFile dxbcFile = { fopen(dxbcFileName, "rb") }; + SDXBCFile outputFile = { fopen(outputFileName, "wb") }; + + bool result = + dxbcFile.m_pFile != NULL && outputFile.m_pFile != NULL && + DXBCCombineWithGLSL(dxbcFile, outputFile, shader); + + if (dxbcFile.m_pFile != NULL) + { + fclose(dxbcFile.m_pFile); + } + if (outputFile.m_pFile != NULL) + { + fclose(outputFile.m_pFile); + } + + return result; +} + +#if !defined(_MSC_VER) +#define sprintf_s(dest, size, ...) sprintf(dest, __VA_ARGS__) +#endif + +#if defined(_WIN32) && defined(PORTABLE) + +DWORD FilterException(DWORD uExceptionCode) +{ + const char* szExceptionName; + char acTemp[10]; + switch (uExceptionCode) + { +#define _CASE(_Name) \ +case _Name: \ + szExceptionName = #_Name; \ + break; + _CASE(EXCEPTION_ACCESS_VIOLATION) + _CASE(EXCEPTION_DATATYPE_MISALIGNMENT) + _CASE(EXCEPTION_BREAKPOINT) + _CASE(EXCEPTION_SINGLE_STEP) + _CASE(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) + _CASE(EXCEPTION_FLT_DENORMAL_OPERAND) + _CASE(EXCEPTION_FLT_DIVIDE_BY_ZERO) + _CASE(EXCEPTION_FLT_INEXACT_RESULT) + _CASE(EXCEPTION_FLT_INVALID_OPERATION) + _CASE(EXCEPTION_FLT_OVERFLOW) + _CASE(EXCEPTION_FLT_STACK_CHECK) + _CASE(EXCEPTION_FLT_UNDERFLOW) + _CASE(EXCEPTION_INT_DIVIDE_BY_ZERO) + _CASE(EXCEPTION_INT_OVERFLOW) + _CASE(EXCEPTION_PRIV_INSTRUCTION) + _CASE(EXCEPTION_IN_PAGE_ERROR) + _CASE(EXCEPTION_ILLEGAL_INSTRUCTION) + _CASE(EXCEPTION_NONCONTINUABLE_EXCEPTION) + _CASE(EXCEPTION_STACK_OVERFLOW) + _CASE(EXCEPTION_INVALID_DISPOSITION) + _CASE(EXCEPTION_GUARD_PAGE) + _CASE(EXCEPTION_INVALID_HANDLE) + //_CASE(EXCEPTION_POSSIBLE_DEADLOCK) +#undef _CASE + default: + sprintf_s(acTemp, "0x%08X", uExceptionCode); + szExceptionName = acTemp; + } + + fprintf(stderr, "Hardware exception thrown (%s)\n", szExceptionName); + return 1; +} + +#endif + +const char* PatchHLSLShaderFile(const char* path) +{ + // Need to transform "half" into "min16float" so FXC preserve min precision to the operands. + static char patchedFileName[MAX_PATH_CHARS]; + const char* defines = "#define half min16float\n" + "#define half2 min16float2\n" + "#define half3 min16float3\n" + "#define half4 min16float4\n"; + + sprintf_s(patchedFileName, sizeof(patchedFileName), "%s.hlslPatched", path); + FILE* shaderFile = fopen(path, "rb"); + if (!shaderFile) + { + return NULL; + } + + FILE* patchedFile = fopen(patchedFileName, "wb"); + if (!patchedFile) + { + return NULL; + } + + // Get size of file + bool result = false; + fseek(shaderFile, 0, SEEK_END); + long size = ftell(shaderFile); + fseek(shaderFile, 0, SEEK_SET); + unsigned char* data = new unsigned char[size + 1]; // Extra byte for the '/0' character. + if (fread(data, 1, size, shaderFile) == size) + { + data[size] = '\0'; + fprintf(patchedFile, "%s%s", defines, data); + result = true; + } + + if (shaderFile) + { + fclose(shaderFile); + } + + if (patchedFile) + { + fclose(patchedFile); + } + + delete[] data; + return result ? patchedFileName : NULL; +} + +int main(int argc, char** argv) +{ + Options options; + +#if defined(_WIN32) && defined(PORTABLE) + __try + { +#endif + + if (!GetOptions(argc, argv, &options)) + { + return 1; + } + + if (options.bUseFxc) + { + char dxbcFileName[MAX_PATH_CHARS]; + char glslFileName[MAX_PATH_CHARS]; + char fullFxcCmdLine[MAX_FXC_CMD_CHARS]; + int retValue; + + if (options.flags & HLSLCC_FLAG_HALF_FLOAT_TRANSFORM) + { + options.shaderFile = PatchHLSLShaderFile(options.shaderFile); + if (!options.shaderFile) + { + return 1; + } + } + + sprintf_s(dxbcFileName, sizeof(dxbcFileName), "%s.dxbc", options.shaderFile); + sprintf_s(glslFileName, sizeof(glslFileName), "%s.patched", options.shaderFile); + + // Need to extract the path to the executable so we can enclose it in quotes + // in case it contains spaces. + const std::string fxcExeName = "fxc.exe"; + + // Case insensitive search + std::string::iterator fxcPos = std::search( + options.fxcCmdLine.begin(), options.fxcCmdLine.end(), + fxcExeName.begin(), fxcExeName.end(), + [](char ch1, char ch2) { return std::tolower(ch1) == std::tolower(ch2); } + ); + + if (fxcPos == options.fxcCmdLine.end()) + { + fprintf(stderr, "Could not find fxc.exe in command line"); + return 1; + } + + // Add the fxcExeName so it gets copied to the fxcExe path. + fxcPos += fxcExeName.length(); + std::string fxcExe(options.fxcCmdLine.begin(), fxcPos); + std::string fxcArguments(fxcPos, options.fxcCmdLine.end()); + +#if defined(APPLE) + fprintf(stderr, "fxc.exe cannot be executed on Mac"); + return 1; +#else + // Need an extra set of quotes around the full command line because the way "system" executes it using cmd. + sprintf_s(fullFxcCmdLine, sizeof(fullFxcCmdLine), "\"\"%s\" %s \"%s\" \"%s\"\"", fxcExe.c_str(), fxcArguments.c_str(), dxbcFileName, options.shaderFile); +#endif + + retValue = system(fullFxcCmdLine); + + if (retValue == 0) + { + GLSLShader shader; + retValue = !Run(dxbcFileName, glslFileName, options.language, options.flags, options.reflectPath, &shader, 1); + + if (retValue == 0) + { + retValue = !CombineDXBCWithGLSL(dxbcFileName, options.outputShaderFile, &shader); + FreeGLSLShader(&shader); + } + } + + remove(dxbcFileName); + remove(glslFileName); + if (options.flags & HLSLCC_FLAG_HALF_FLOAT_TRANSFORM) + { + // Removed the hlsl patched file that was created. + remove(options.shaderFile); + } + + return retValue; + } + + if (options.shaderFile) + { + if (!Run(options.shaderFile, options.outputShaderFile, options.language, options.flags, options.reflectPath, NULL, 0)) + { + return 1; + } + } + +#if defined(_WIN32) && defined(PORTABLE) +} +__except (FilterException(GetExceptionCode())) +{ + return 1; +} +#endif + + + return 0; +} diff --git a/Code/Tools/HLSLCrossCompiler/offline/hash.h b/Code/Tools/HLSLCrossCompiler/offline/hash.h new file mode 100644 index 0000000000..f93f3b65d3 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/hash.h @@ -0,0 +1,152 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef HASH_H_ +#define HASH_H_ + +/* +-------------------------------------------------------------------- +mix -- mix 3 64-bit values reversibly. +mix() takes 48 machine instructions, but only 24 cycles on a superscalar + machine (like Intel's new MMX architecture). It requires 4 64-bit + registers for 4::2 parallelism. +All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of + (a,b,c), and all deltas of bottom bits were tested. All deltas were + tested both on random keys and on keys that were nearly all zero. + These deltas all cause every bit of c to change between 1/3 and 2/3 + of the time (well, only 113/400 to 287/400 of the time for some + 2-bit delta). These deltas all cause at least 80 bits to change + among (a,b,c) when the mix is run either forward or backward (yes it + is reversible). +This implies that a hash using mix64 has no funnels. There may be + characteristics with 3-bit deltas or bigger, I didn't test for + those. +-------------------------------------------------------------------- +*/ +#define mix64(a, b, c) \ + { \ + a -= b; a -= c; a ^= (c >> 43); \ + b -= c; b -= a; b ^= (a << 9); \ + c -= a; c -= b; c ^= (b >> 8); \ + a -= b; a -= c; a ^= (c >> 38); \ + b -= c; b -= a; b ^= (a << 23); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 35); \ + b -= c; b -= a; b ^= (a << 49); \ + c -= a; c -= b; c ^= (b >> 11); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 18); \ + c -= a; c -= b; c ^= (b >> 22); \ + } + +/* +-------------------------------------------------------------------- +hash64() -- hash a variable-length key into a 64-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 8-byte value +Returns a 64-bit value. Every bit of the key affects every bit of +the return value. No funnels. Every 1-bit and 2-bit delta achieves +avalanche. About 41+5len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 64 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i= 24) + { + a += (k[0] + ((uint64_t)k[ 1] << 8) + ((uint64_t)k[ 2] << 16) + ((uint64_t)k[ 3] << 24) + + ((uint64_t)k[4 ] << 32) + ((uint64_t)k[ 5] << 40) + ((uint64_t)k[ 6] << 48) + ((uint64_t)k[ 7] << 56)); + b += (k[8] + ((uint64_t)k[ 9] << 8) + ((uint64_t)k[10] << 16) + ((uint64_t)k[11] << 24) + + ((uint64_t)k[12] << 32) + ((uint64_t)k[13] << 40) + ((uint64_t)k[14] << 48) + ((uint64_t)k[15] << 56)); + c += (k[16] + ((uint64_t)k[17] << 8) + ((uint64_t)k[18] << 16) + ((uint64_t)k[19] << 24) + + ((uint64_t)k[20] << 32) + ((uint64_t)k[21] << 40) + ((uint64_t)k[22] << 48) + ((uint64_t)k[23] << 56)); + mix64(a, b, c); + k += 24; + len -= 24; + } + + /*------------------------------------- handle the last 23 bytes */ + c += length; + switch (len) /* all the case statements fall through */ + { + case 23: + c += ((uint64_t)k[22] << 56); + case 22: + c += ((uint64_t)k[21] << 48); + case 21: + c += ((uint64_t)k[20] << 40); + case 20: + c += ((uint64_t)k[19] << 32); + case 19: + c += ((uint64_t)k[18] << 24); + case 18: + c += ((uint64_t)k[17] << 16); + case 17: + c += ((uint64_t)k[16] << 8); + /* the first byte of c is reserved for the length */ + case 16: + b += ((uint64_t)k[15] << 56); + case 15: + b += ((uint64_t)k[14] << 48); + case 14: + b += ((uint64_t)k[13] << 40); + case 13: + b += ((uint64_t)k[12] << 32); + case 12: + b += ((uint64_t)k[11] << 24); + case 11: + b += ((uint64_t)k[10] << 16); + case 10: + b += ((uint64_t)k[ 9] << 8); + case 9: + b += ((uint64_t)k[ 8]); + case 8: + a += ((uint64_t)k[ 7] << 56); + case 7: + a += ((uint64_t)k[ 6] << 48); + case 6: + a += ((uint64_t)k[ 5] << 40); + case 5: + a += ((uint64_t)k[ 4] << 32); + case 4: + a += ((uint64_t)k[ 3] << 24); + case 3: + a += ((uint64_t)k[ 2] << 16); + case 2: + a += ((uint64_t)k[ 1] << 8); + case 1: + a += ((uint64_t)k[ 0]); + /* case 0: nothing left to add */ + } + mix64(a, b, c); + /*-------------------------------------------- report the result */ + return c; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/offline/serializeReflection.cpp b/Code/Tools/HLSLCrossCompiler/offline/serializeReflection.cpp new file mode 100644 index 0000000000..15fe8d5b96 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/serializeReflection.cpp @@ -0,0 +1,207 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "serializeReflection.h" +#include "cJSON.h" +#include +#include + +void* jsonMalloc(size_t sz) +{ + return new char[sz]; +} +void jsonFree(void* ptr) +{ + char* charPtr = static_cast(ptr); + delete [] charPtr; +} + +static void AppendIntToString(std::string& str, uint32_t num) +{ + std::stringstream ss; + ss << num; + str += ss.str(); +} + +static void WriteInOutSignature(InOutSignature* psSignature, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "SemanticName", cJSON_CreateString(psSignature->SemanticName)); + cJSON_AddItemToObject(obj, "ui32SemanticIndex", cJSON_CreateNumber(psSignature->ui32SemanticIndex)); + cJSON_AddItemToObject(obj, "eSystemValueType", cJSON_CreateNumber(psSignature->eSystemValueType)); + cJSON_AddItemToObject(obj, "eComponentType", cJSON_CreateNumber(psSignature->eComponentType)); + cJSON_AddItemToObject(obj, "ui32Register", cJSON_CreateNumber(psSignature->ui32Register)); + cJSON_AddItemToObject(obj, "ui32Mask", cJSON_CreateNumber(psSignature->ui32Mask)); + cJSON_AddItemToObject(obj, "ui32ReadWriteMask", cJSON_CreateNumber(psSignature->ui32ReadWriteMask)); +} + +static void WriteResourceBinding(ResourceBinding* psBinding, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psBinding->Name)); + cJSON_AddItemToObject(obj, "eType", cJSON_CreateNumber(psBinding->eType)); + cJSON_AddItemToObject(obj, "ui32BindPoint", cJSON_CreateNumber(psBinding->ui32BindPoint)); + cJSON_AddItemToObject(obj, "ui32BindCount", cJSON_CreateNumber(psBinding->ui32BindCount)); + cJSON_AddItemToObject(obj, "ui32Flags", cJSON_CreateNumber(psBinding->ui32Flags)); + cJSON_AddItemToObject(obj, "eDimension", cJSON_CreateNumber(psBinding->eDimension)); + cJSON_AddItemToObject(obj, "ui32ReturnType", cJSON_CreateNumber(psBinding->ui32ReturnType)); + cJSON_AddItemToObject(obj, "ui32NumSamples", cJSON_CreateNumber(psBinding->ui32NumSamples)); +} + +static void WriteShaderVar(ShaderVar* psVar, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psVar->Name)); + if(psVar->haveDefaultValue) + { + cJSON_AddItemToObject(obj, "aui32DefaultValues", cJSON_CreateIntArray((int*)psVar->pui32DefaultValues, psVar->ui32Size/4)); + } + cJSON_AddItemToObject(obj, "ui32StartOffset", cJSON_CreateNumber(psVar->ui32StartOffset)); + cJSON_AddItemToObject(obj, "ui32Size", cJSON_CreateNumber(psVar->ui32Size)); +} + +static void WriteConstantBuffer(ConstantBuffer* psCBuf, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psCBuf->Name)); + cJSON_AddItemToObject(obj, "ui32NumVars", cJSON_CreateNumber(psCBuf->ui32NumVars)); + + for(uint32_t i = 0; i < psCBuf->ui32NumVars; ++i) + { + std::string name; + name += "var"; + AppendIntToString(name, i); + + cJSON* varObj = cJSON_CreateObject(); + cJSON_AddItemToObject(obj, name.c_str(), varObj); + + WriteShaderVar(&psCBuf->asVars[i], varObj); + } + + cJSON_AddItemToObject(obj, "ui32TotalSizeInBytes", cJSON_CreateNumber(psCBuf->ui32TotalSizeInBytes)); +} + +static void WriteClassType(ClassType* psClassType, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psClassType->Name)); + cJSON_AddItemToObject(obj, "ui16ID", cJSON_CreateNumber(psClassType->ui16ID)); + cJSON_AddItemToObject(obj, "ui16ConstBufStride", cJSON_CreateNumber(psClassType->ui16ConstBufStride)); + cJSON_AddItemToObject(obj, "ui16Texture", cJSON_CreateNumber(psClassType->ui16Texture)); + cJSON_AddItemToObject(obj, "ui16Sampler", cJSON_CreateNumber(psClassType->ui16Sampler)); +} + +static void WriteClassInstance(ClassInstance* psClassInst, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psClassInst->Name)); + cJSON_AddItemToObject(obj, "ui16ID", cJSON_CreateNumber(psClassInst->ui16ID)); + cJSON_AddItemToObject(obj, "ui16ConstBuf", cJSON_CreateNumber(psClassInst->ui16ConstBuf)); + cJSON_AddItemToObject(obj, "ui16ConstBufOffset", cJSON_CreateNumber(psClassInst->ui16ConstBufOffset)); + cJSON_AddItemToObject(obj, "ui16Texture", cJSON_CreateNumber(psClassInst->ui16Texture)); + cJSON_AddItemToObject(obj, "ui16Sampler", cJSON_CreateNumber(psClassInst->ui16Sampler)); +} + +const char* SerializeReflection(ShaderInfo* psReflection) +{ + cJSON* root; + + cJSON_Hooks hooks; + hooks.malloc_fn = jsonMalloc; + hooks.free_fn = jsonFree; + cJSON_InitHooks(&hooks); + + root=cJSON_CreateObject(); + cJSON_AddItemToObject(root, "ui32MajorVersion", cJSON_CreateNumber(psReflection->ui32MajorVersion)); + cJSON_AddItemToObject(root, "ui32MinorVersion", cJSON_CreateNumber(psReflection->ui32MinorVersion)); + + cJSON_AddItemToObject(root, "ui32NumInputSignatures", cJSON_CreateNumber(psReflection->ui32NumInputSignatures)); + + for(uint32_t i = 0; i < psReflection->ui32NumInputSignatures; ++i) + { + std::string name; + name += "input"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteInOutSignature(psReflection->psInputSignatures+i, obj); + } + + cJSON_AddItemToObject(root, "ui32NumOutputSignatures", cJSON_CreateNumber(psReflection->ui32NumOutputSignatures)); + + for(uint32_t i = 0; i < psReflection->ui32NumOutputSignatures; ++i) + { + std::string name; + name += "output"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteInOutSignature(psReflection->psOutputSignatures+i, obj); + } + + cJSON_AddItemToObject(root, "ui32NumResourceBindings", cJSON_CreateNumber(psReflection->ui32NumResourceBindings)); + + for(uint32_t i = 0; i < psReflection->ui32NumResourceBindings; ++i) + { + std::string name; + name += "resource"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteResourceBinding(psReflection->psResourceBindings+i, obj); + } + + cJSON_AddItemToObject(root, "ui32NumConstantBuffers", cJSON_CreateNumber(psReflection->ui32NumConstantBuffers)); + + for(uint32_t i = 0; i < psReflection->ui32NumConstantBuffers; ++i) + { + std::string name; + name += "cbuf"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteConstantBuffer(psReflection->psConstantBuffers+i, obj); + } + + //psThisPointerConstBuffer is a cache. Don't need to write this out. + //It just points to the $ThisPointer cbuffer within the psConstantBuffers array. + + for(uint32_t i = 0; i < psReflection->ui32NumClassTypes; ++i) + { + std::string name; + name += "classType"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteClassType(psReflection->psClassTypes+i, obj); + } + + for(uint32_t i = 0; i < psReflection->ui32NumClassInstances; ++i) + { + std::string name; + name += "classInst"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteClassInstance(psReflection->psClassInstances+i, obj); + } + + //psReflection->aui32TableIDToTypeID + //psReflection->aui32ConstBufferBindpointRemap + + cJSON_AddItemToObject(root, "eTessPartitioning", cJSON_CreateNumber(psReflection->eTessPartitioning)); + cJSON_AddItemToObject(root, "eTessOutPrim", cJSON_CreateNumber(psReflection->eTessOutPrim)); + + + const char* jsonString = cJSON_Print(root); + + cJSON_Delete(root); + + return jsonString; +} diff --git a/Code/Tools/HLSLCrossCompiler/offline/serializeReflection.h b/Code/Tools/HLSLCrossCompiler/offline/serializeReflection.h new file mode 100644 index 0000000000..c8c4175a6a --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/serializeReflection.h @@ -0,0 +1,11 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef SERIALIZE_REFLECTION_H_ +#define SERIALIZE_REFLECTION_H_ + +#include "hlslcc.h" + +const char* SerializeReflection(ShaderInfo* psReflection); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/offline/timer.cpp b/Code/Tools/HLSLCrossCompiler/offline/timer.cpp new file mode 100644 index 0000000000..c707e1bfa8 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/timer.cpp @@ -0,0 +1,40 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "timer.h" + +void InitTimer(Timer_t* psTimer) +{ +#if defined(_WIN32) + QueryPerformanceFrequency(&psTimer->frequency); +#endif +} + +void ResetTimer(Timer_t* psTimer) +{ +#if defined(_WIN32) + QueryPerformanceCounter(&psTimer->startCount); +#else + gettimeofday(&psTimer->startCount, 0); +#endif +} + +/* Returns time in micro seconds */ +double ReadTimer(Timer_t* psTimer) +{ + double startTimeInMicroSec, endTimeInMicroSec; + +#if defined(_WIN32) + const double freq = (1000000.0 / psTimer->frequency.QuadPart); + QueryPerformanceCounter(&psTimer->endCount); + startTimeInMicroSec = psTimer->startCount.QuadPart * freq; + endTimeInMicroSec = psTimer->endCount.QuadPart * freq; +#else + gettimeofday(&psTimer->endCount, 0); + startTimeInMicroSec = (psTimer->startCount.tv_sec * 1000000.0) + psTimer->startCount.tv_usec; + endTimeInMicroSec = (psTimer->endCount.tv_sec * 1000000.0) + psTimer->endCount.tv_usec; +#endif + + return endTimeInMicroSec - startTimeInMicroSec; +} + diff --git a/Code/Tools/HLSLCrossCompiler/offline/timer.h b/Code/Tools/HLSLCrossCompiler/offline/timer.h new file mode 100644 index 0000000000..3f4ea333fd --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/offline/timer.h @@ -0,0 +1,29 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TIMER_H +#define TIMER_H + +#ifdef _WIN32 +#include +#else +#include +#endif + +typedef struct +{ +#ifdef _WIN32 + LARGE_INTEGER frequency; + LARGE_INTEGER startCount; + LARGE_INTEGER endCount; +#else + struct timeval startCount; + struct timeval endCount; +#endif +} Timer_t; + +void InitTimer(Timer_t* psTimer); +void ResetTimer(Timer_t* psTimer); +double ReadTimer(Timer_t* psTimer); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/amazon_changes.c b/Code/Tools/HLSLCrossCompiler/src/amazon_changes.c new file mode 100644 index 0000000000..7b339ba93e --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/amazon_changes.c @@ -0,0 +1,219 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toGLSLInstruction.h" +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/languages.h" +#include "bstrlib.h" +#include "stdio.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include "amazon_changes.h" + +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wpointer-sign" +#endif + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); + +// These are .c files, so no C++ or C++11 for us :( +#define MAX_VARIABLE_LENGTH 16 + +// This struct is used to keep track of each valid occurance of xxxBitsToxxx(variable) and store all relevant information for fixing that instance +typedef struct ShaderCastLocation +{ + char tempVariableName[MAX_VARIABLE_LENGTH]; + char replacementVariableName[MAX_VARIABLE_LENGTH]; + unsigned int castType; + + // Since we have no stl, here's our list + struct ShaderCastLocation* next; +} ShaderCastLocation; + +// Structure used to prebuild the list of all functions that need to be replaced. +typedef struct ShaderCastType +{ + const char* functionName; + unsigned int castType; + const char* variableTypeName; // String for the variable type used when declaring a temporary variable to replace the source temp vector +} ShaderCastType; + +enum ShaderCasts +{ + CAST_UINTBITSTOFLOAT, + CAST_INTBITSTOFLOAT, + CAST_FLOATBITSTOUINT, + CAST_FLOATBITSTOINT, + CAST_NUMCASTS +}; + +// NOTICE: Order is important here because intBitsToFloat is a substring of uintBitsToFloat, so do not change the ordering here! +static const ShaderCastType s_castFunctions[CAST_NUMCASTS] = +{ + { "uintBitsToFloat", CAST_UINTBITSTOFLOAT, "uvec4" }, + { "intBitsToFloat", CAST_INTBITSTOFLOAT, "ivec4" }, + { "floatBitsToUint", CAST_FLOATBITSTOUINT, "vec4" }, + { "floatBitsToInt", CAST_FLOATBITSTOINT, "vec4" } +}; + +int IsValidUseCase( char* variableStart, char* outVariableName, ShaderCastLocation* foundShaderCastsHead, int currentType ) +{ + // Cases we have to replace (this is very strict in definition): + // 1) floatBitsToInt(Temp2) + // 2) floatBitsToInt(Temp2.x) + // 3) floatBitsToInt(Temp[0]) + // 4) floatBitsToInt(Temp[0].x) + // Cases we do not have to replace: + // 1) floatBitsToInt(vec4(Temp2)) + // 2) floatBitsToInt(Output0.x != 0.0f ? 1.0f : 0.0f) + // 3) Any other version that evaluates an expression within the () + if ( strncmp(variableStart, "Temp", 4) != 0 ) + return 0; + + unsigned int lengthOfVariable = 4; // Start at 4 for temp + + while ( 1 ) + { + char val = *(variableStart + lengthOfVariable); + + // If alphanumeric or [] (array), we have a valid variable name + if ( isalnum( val ) || (val == '[') || (val == ']') ) + { + lengthOfVariable++; + } + else if ( (val == ')') || (val == '.') ) + { + // Found end of variable + break; + } + else + { + // Found something unexpected, so abort + return 0; + } + } + + ASSERT( lengthOfVariable < MAX_VARIABLE_LENGTH ); + + // Now ensure that no duplicates of this declaration already exist + ShaderCastLocation* currentLink = foundShaderCastsHead; + while ( currentLink ) + { + // If we have the same type and the same name + if ( (currentType == currentLink->castType) && (strncmp(variableStart, currentLink->tempVariableName, lengthOfVariable) == 0) ) + return 0; // Do not add because an entry already exists for this variable and this cast function + + // Hmm...I guess this scenario is possible, but it has not shown up in any shaders. + // The only time we could ever hit this is if the same line casts a float to both an int and uint in separate calls + // Seems highly unlikely, so let's just assert for now and fix it if we have to. + if ( strncmp(variableStart, currentLink->tempVariableName, lengthOfVariable) == 0 ) + { + // TODO: Implement this case where we cast the same variable to multiple types on the same line of GLSL + ASSERT(0); + } + + currentLink = currentLink->next; + } + + // We found a unique instance, so store it + strncpy( outVariableName, variableStart, lengthOfVariable ); + return 1; +} + +void ModifyLineForQualcommReinterpretCastBug( HLSLCrossCompilerContext* psContext, bstring* originalString, bstring* overloadString ) +{ + unsigned int numFoundCasts = 0; + + ShaderCastLocation* foundShaderCastsHead = NULL; + ShaderCastLocation* currentShaderCasts = NULL; + + // Find all occurances of the *BitsTo* functions + // Note that this would be cleaner, but 'intBitsToFloat' is a substring of 'uintBitsToFloat' so parsing order is important here. + char* parsingString = bdataofs(*overloadString, 0); + while ( parsingString ) + { + char* result = NULL; + + for ( int index=0; indexnext ); + currentShaderCasts->next = (ShaderCastLocation*)hlslcc_malloc( sizeof(ShaderCastLocation) ); + memset( currentShaderCasts->next, 0x0, sizeof(ShaderCastLocation) ); + currentShaderCasts = currentShaderCasts->next; + } + + currentShaderCasts->castType = index; + strcpy( currentShaderCasts->tempVariableName, tempVariableName ); + + numFoundCasts++; + } + result += strlen( s_castFunctions[index].functionName ); + + // Break out of the loop because we have to advance the search string and start over with uintBitsToFloat again due to the problem with intBitsToFloat being a substring + break; + } + } + + parsingString = result; + } + + // If we have found no casts, then append the line to the primary string + if ( numFoundCasts == 0 ) + { + bconcat( *originalString, *overloadString ); + return; + } + + // Now we start creating our temporary variables to workaround the crash + currentShaderCasts = foundShaderCastsHead; + + // NOTE: We want a count of all variables processed for this entire shader. This could be fancier... + static unsigned int currentVariableIndex = 0; + + while ( currentShaderCasts ) + { + // Generate new variable name + sprintf( currentShaderCasts->replacementVariableName, "LYTemp%i", currentVariableIndex ); + + // Write out the new variable name declaration and initialize it + AddIndentation( psContext ); + bformata( *originalString, "%s %s=%s;\n", s_castFunctions[currentShaderCasts->castType].variableTypeName, currentShaderCasts->replacementVariableName, currentShaderCasts->tempVariableName ); + + // Now replace all instances of the variable in question with the new variable name. + // Note: We can't do a breplace on the temp variable name because the variable can still be legally used without a reinterpret cast in that line. + // Do a full replace on the xxBitsToxx(TempVar) here + bstring tempVarName = bformat( "%s(%s)", s_castFunctions[currentShaderCasts->castType].functionName, currentShaderCasts->tempVariableName ); + bstring replacementVarName = bformat( "%s(%s)", s_castFunctions[currentShaderCasts->castType].functionName, currentShaderCasts->replacementVariableName ); + bfindreplace( *overloadString, tempVarName, replacementVarName, 0 ); + + // Cleanup bstrings allocated from bformat + bdestroy( tempVarName ); + bdestroy( replacementVarName ); + + currentVariableIndex++; + currentShaderCasts = currentShaderCasts->next; + } + + // Now append our modified string to the full shader file + bconcat( *originalString, *overloadString ); +} diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.c b/Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.c new file mode 100644 index 0000000000..3f24fa3341 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.c @@ -0,0 +1,20 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bsafe.c + * + * This is an optional module that can be used to help enforce a safety + * standard based on pervasive usage of bstrlib. This file is not necessarily + * portable, however, it has been tested to work correctly with Intel's C/C++ + * compiler, WATCOM C/C++ v11.x and Microsoft Visual C++. + */ + +#include +#include +#include "bsafe.h" diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.h b/Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.h new file mode 100644 index 0000000000..3a647a6ac8 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bsafe.h @@ -0,0 +1,45 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bsafe.h + * + * This is an optional module that can be used to help enforce a safety + * standard based on pervasive usage of bstrlib. This file is not necessarily + * portable, however, it has been tested to work correctly with Intel's C/C++ + * compiler, WATCOM C/C++ v11.x and Microsoft Visual C++. + */ + +#ifndef BSTRLIB_BSAFE_INCLUDE +#define BSTRLIB_BSAFE_INCLUDE + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__GNUC__) && !defined(__clang__) +#if !defined (__GNUC__) && (!defined(_MSC_VER) || (_MSC_VER <= 1310)) +/* This is caught in the linker, so its not necessary for gcc. */ +extern char * (gets) (char * buf); +#endif + +extern char * (strncpy) (char *dst, const char *src, size_t n); +extern char * (strncat) (char *dst, const char *src, size_t n); +extern char * (strtok) (char *s1, const char *s2); +extern char * (strdup) (const char *s); + +#undef strcpy +#undef strcat +#define strcpy(a,b) bsafe_strcpy(a,b) +#define strcat(a,b) bsafe_strcat(a,b) +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.c b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.c new file mode 100644 index 0000000000..2dc7b04840 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.c @@ -0,0 +1,1134 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstraux.c + * + * This file is not necessarily part of the core bstring library itself, but + * is just an auxilliary module which includes miscellaneous or trivial + * functions. + */ + +#include +#include +#include +#include +#include +#include "bstrlib.h" +#include "bstraux.h" + +/* bstring bTail (bstring b, int n) + * + * Return with a string of the last n characters of b. + */ +bstring bTail (bstring b, int n) { + if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL; + if (n >= b->slen) return bstrcpy (b); + return bmidstr (b, b->slen - n, n); +} + +/* bstring bHead (bstring b, int n) + * + * Return with a string of the first n characters of b. + */ +bstring bHead (bstring b, int n) { + if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL; + if (n >= b->slen) return bstrcpy (b); + return bmidstr (b, 0, n); +} + +/* int bFill (bstring a, char c, int len) + * + * Fill a given bstring with the character in parameter c, for a length n. + */ +int bFill (bstring b, char c, int len) { + if (b == NULL || len < 0 || (b->mlen < b->slen && b->mlen > 0)) return -__LINE__; + b->slen = 0; + return bsetstr (b, len, NULL, c); +} + +/* int bReplicate (bstring b, int n) + * + * Replicate the contents of b end to end n times and replace it in b. + */ +int bReplicate (bstring b, int n) { + return bpattern (b, n * b->slen); +} + +/* int bReverse (bstring b) + * + * Reverse the contents of b in place. + */ +int bReverse (bstring b) { +int i, n, m; +unsigned char t; + + if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__; + n = b->slen; + if (2 <= n) { + m = ((unsigned)n) >> 1; + n--; + for (i=0; i < m; i++) { + t = b->data[n - i]; + b->data[n - i] = b->data[i]; + b->data[i] = t; + } + } + return 0; +} + +/* int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) + * + * Insert a repeated sequence of a given character into the string at + * position pos for a length len. + */ +int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) { + if (b == NULL || b->slen < 0 || b->mlen < b->slen || pos < 0 || len <= 0) return -__LINE__; + + if (pos > b->slen + && 0 > bsetstr (b, pos, NULL, fill)) return -__LINE__; + + if (0 > balloc (b, b->slen + len)) return -__LINE__; + if (pos < b->slen) memmove (b->data + pos + len, b->data + pos, b->slen - pos); + memset (b->data + pos, c, len); + b->slen += len; + b->data[b->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bJustifyLeft (bstring b, int space) + * + * Left justify a string. + */ +int bJustifyLeft (bstring b, int space) { +int j, i, s, t; +unsigned char c = (unsigned char) space; + + if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__; + if (space != (int) c) return BSTR_OK; + + for (s=j=i=0; i < b->slen; i++) { + t = s; + s = c != (b->data[j] = b->data[i]); + j += (t|s); + } + if (j > 0 && b->data[j-1] == c) j--; + + b->data[j] = (unsigned char) '\0'; + b->slen = j; + return BSTR_OK; +} + +/* int bJustifyRight (bstring b, int width, int space) + * + * Right justify a string to within a given width. + */ +int bJustifyRight (bstring b, int width, int space) { +int ret; + if (width <= 0) return -__LINE__; + if (0 > (ret = bJustifyLeft (b, space))) return ret; + if (b->slen <= width) + return bInsertChrs (b, 0, width - b->slen, (unsigned char) space, (unsigned char) space); + return BSTR_OK; +} + +/* int bJustifyCenter (bstring b, int width, int space) + * + * Center a string's non-white space characters to within a given width by + * inserting whitespaces at the beginning. + */ +int bJustifyCenter (bstring b, int width, int space) { +int ret; + if (width <= 0) return -__LINE__; + if (0 > (ret = bJustifyLeft (b, space))) return ret; + if (b->slen <= width) + return bInsertChrs (b, 0, (width - b->slen + 1) >> 1, (unsigned char) space, (unsigned char) space); + return BSTR_OK; +} + +/* int bJustifyMargin (bstring b, int width, int space) + * + * Stretch a string to flush against left and right margins by evenly + * distributing additional white space between words. If the line is too + * long to be margin justified, it is left justified. + */ +int bJustifyMargin (bstring b, int width, int space) { +struct bstrList * sl; +int i, l, c; + + if (b == NULL || b->slen < 0 || b->mlen == 0 || b->mlen < b->slen) return -__LINE__; + if (NULL == (sl = bsplit (b, (unsigned char) space))) return -__LINE__; + for (l=c=i=0; i < sl->qty; i++) { + if (sl->entry[i]->slen > 0) { + c ++; + l += sl->entry[i]->slen; + } + } + + if (l + c >= width || c < 2) { + bstrListDestroy (sl); + return bJustifyLeft (b, space); + } + + b->slen = 0; + for (i=0; i < sl->qty; i++) { + if (sl->entry[i]->slen > 0) { + if (b->slen > 0) { + int s = (width - l + (c / 2)) / c; + bInsertChrs (b, b->slen, s, (unsigned char) space, (unsigned char) space); + l += s; + } + bconcat (b, sl->entry[i]); + c--; + if (c <= 0) break; + } + } + + bstrListDestroy (sl); + return BSTR_OK; +} + +static size_t readNothing (void *buff, size_t elsize, size_t nelem, void *parm) { + buff = buff; + elsize = elsize; + nelem = nelem; + parm = parm; + return 0; /* Immediately indicate EOF. */ +} + +/* struct bStream * bsFromBstr (const_bstring b); + * + * Create a bStream whose contents are a copy of the bstring passed in. + * This allows the use of all the bStream APIs with bstrings. + */ +struct bStream * bsFromBstr (const_bstring b) { +struct bStream * s = bsopen ((bNread) readNothing, NULL); + bsunread (s, b); /* Push the bstring data into the empty bStream. */ + return s; +} + +static size_t readRef (void *buff, size_t elsize, size_t nelem, void *parm) { +struct tagbstring * t = (struct tagbstring *) parm; +size_t tsz = elsize * nelem; + + if (tsz > (size_t) t->slen) tsz = (size_t) t->slen; + if (tsz > 0) { + memcpy (buff, t->data, tsz); + t->slen -= (int) tsz; + t->data += tsz; + return tsz / elsize; + } + return 0; +} + +/* The "by reference" version of the above function. This function puts + * a number of restrictions on the call site (the passed in struct + * tagbstring *will* be modified by this function, and the source data + * must remain alive and constant for the lifetime of the bStream). + * Hence it is not presented as an extern. + */ +static struct bStream * bsFromBstrRef (struct tagbstring * t) { + if (!t) return NULL; + return bsopen ((bNread) readRef, t); +} + +/* char * bStr2NetStr (const_bstring b) + * + * Convert a bstring to a netstring. See + * http://cr.yp.to/proto/netstrings.txt for a description of netstrings. + * Note: 1) The value returned should be freed with a call to bcstrfree() at + * the point when it will no longer be referenced to avoid a memory + * leak. + * 2) If the returned value is non-NULL, then it also '\0' terminated + * in the character position one past the "," terminator. + */ +char * bStr2NetStr (const_bstring b) { +char strnum[sizeof (b->slen) * 3 + 1]; +bstring s; +unsigned char * buff; + + if (b == NULL || b->data == NULL || b->slen < 0) return NULL; + sprintf (strnum, "%d:", b->slen); + if (NULL == (s = bfromcstr (strnum)) + || bconcat (s, b) == BSTR_ERR || bconchar (s, (char) ',') == BSTR_ERR) { + bdestroy (s); + return NULL; + } + buff = s->data; + bcstrfree ((char *) s); + return (char *) buff; +} + +/* bstring bNetStr2Bstr (const char * buf) + * + * Convert a netstring to a bstring. See + * http://cr.yp.to/proto/netstrings.txt for a description of netstrings. + * Note that the terminating "," *must* be present, however a following '\0' + * is *not* required. + */ +bstring bNetStr2Bstr (const char * buff) { +int i, x; +bstring b; + if (buff == NULL) return NULL; + x = 0; + for (i=0; buff[i] != ':'; i++) { + unsigned int v = buff[i] - '0'; + if (v > 9 || x > ((INT_MAX - (signed int)v) / 10)) return NULL; + x = (x * 10) + v; + } + + /* This thing has to be properly terminated */ + if (buff[i + 1 + x] != ',') return NULL; + + if (NULL == (b = bfromcstr (""))) return NULL; + if (balloc (b, x + 1) != BSTR_OK) { + bdestroy (b); + return NULL; + } + memcpy (b->data, buff + i + 1, x); + b->data[x] = (unsigned char) '\0'; + b->slen = x; + return b; +} + +static char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* bstring bBase64Encode (const_bstring b) + * + * Generate a base64 encoding. See: RFC1341 + */ +bstring bBase64Encode (const_bstring b) { +int i, c0, c1, c2, c3; +bstring out; + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + + out = bfromcstr (""); + for (i=0; i + 2 < b->slen; i += 3) { + if (i && ((i % 57) == 0)) { + if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) { + bdestroy (out); + return NULL; + } + } + c0 = b->data[i] >> 2; + c1 = ((b->data[i] << 4) | + (b->data[i+1] >> 4)) & 0x3F; + c2 = ((b->data[i+1] << 2) | + (b->data[i+2] >> 6)) & 0x3F; + c3 = b->data[i+2] & 0x3F; + if (bconchar (out, b64ETable[c0]) < 0 || + bconchar (out, b64ETable[c1]) < 0 || + bconchar (out, b64ETable[c2]) < 0 || + bconchar (out, b64ETable[c3]) < 0) { + bdestroy (out); + return NULL; + } + } + + if (i && ((i % 57) == 0)) { + if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) { + bdestroy (out); + return NULL; + } + } + + switch (i + 2 - b->slen) { + case 0: c0 = b->data[i] >> 2; + c1 = ((b->data[i] << 4) | + (b->data[i+1] >> 4)) & 0x3F; + c2 = (b->data[i+1] << 2) & 0x3F; + if (bconchar (out, b64ETable[c0]) < 0 || + bconchar (out, b64ETable[c1]) < 0 || + bconchar (out, b64ETable[c2]) < 0 || + bconchar (out, (char) '=') < 0) { + bdestroy (out); + return NULL; + } + break; + case 1: c0 = b->data[i] >> 2; + c1 = (b->data[i] << 4) & 0x3F; + if (bconchar (out, b64ETable[c0]) < 0 || + bconchar (out, b64ETable[c1]) < 0 || + bconchar (out, (char) '=') < 0 || + bconchar (out, (char) '=') < 0) { + bdestroy (out); + return NULL; + } + break; + case 2: break; + } + + return out; +} + +#define B64_PAD (-2) +#define B64_ERR (-1) + +static int base64DecodeSymbol (unsigned char alpha) { + if ((alpha >= 'A') && (alpha <= 'Z')) return (int)(alpha - 'A'); + else if ((alpha >= 'a') && (alpha <= 'z')) + return 26 + (int)(alpha - 'a'); + else if ((alpha >= '0') && (alpha <= '9')) + return 52 + (int)(alpha - '0'); + else if (alpha == '+') return 62; + else if (alpha == '/') return 63; + else if (alpha == '=') return B64_PAD; + else return B64_ERR; +} + +/* bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) + * + * Decode a base64 block of data. All MIME headers are assumed to have been + * removed. See: RFC1341 + */ +bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) { +int i, v; +unsigned char c0, c1, c2; +bstring out; + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + if (boolTruncError) *boolTruncError = 0; + out = bfromcstr (""); + i = 0; + for (;;) { + do { + if (i >= b->slen) return out; + if (b->data[i] == '=') { /* Bad "too early" truncation */ + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c0 = (unsigned char) (v << 2); + do { + if (i >= b->slen || b->data[i] == '=') { /* Bad "too early" truncation */ + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c0 |= (unsigned char) (v >> 4); + c1 = (unsigned char) (v << 4); + do { + if (i >= b->slen) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + if (b->data[i] == '=') { + i++; + if (i >= b->slen || b->data[i] != '=' || bconchar (out, c0) < 0) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); /* Missing "=" at the end. */ + return NULL; + } + return out; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c1 |= (unsigned char) (v >> 2); + c2 = (unsigned char) (v << 6); + do { + if (i >= b->slen) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + if (b->data[i] == '=') { + if (bconchar (out, c0) < 0 || bconchar (out, c1) < 0) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + if (boolTruncError) *boolTruncError = 0; + return out; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c2 |= (unsigned char) (v); + if (bconchar (out, c0) < 0 || + bconchar (out, c1) < 0 || + bconchar (out, c2) < 0) { + if (boolTruncError) { + *boolTruncError = -1; + return out; + } + bdestroy (out); + return NULL; + } + } +} + +#define UU_DECODE_BYTE(b) (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ') + +struct bUuInOut { + bstring src, dst; + int * badlines; +}; + +#define UU_MAX_LINELEN 45 + +static int bUuDecLine (void * parm, int ofs, int len) { +struct bUuInOut * io = (struct bUuInOut *) parm; +bstring s = io->src; +bstring t = io->dst; +int i, llen, otlen, ret, c0, c1, c2, c3, d0, d1, d2, d3; + + if (len == 0) return 0; + llen = UU_DECODE_BYTE (s->data[ofs]); + ret = 0; + + otlen = t->slen; + + if (((unsigned) llen) > UU_MAX_LINELEN) { ret = -__LINE__; + goto bl; + } + + llen += t->slen; + + for (i=1; i < s->slen && t->slen < llen;i += 4) { + unsigned char outoctet[3]; + c0 = UU_DECODE_BYTE (d0 = (int) bchare (s, i+ofs+0, ' ' - 1)); + c1 = UU_DECODE_BYTE (d1 = (int) bchare (s, i+ofs+1, ' ' - 1)); + c2 = UU_DECODE_BYTE (d2 = (int) bchare (s, i+ofs+2, ' ' - 1)); + c3 = UU_DECODE_BYTE (d3 = (int) bchare (s, i+ofs+3, ' ' - 1)); + + if (((unsigned) (c0|c1) >= 0x40)) { if (!ret) ret = -__LINE__; + if (d0 > 0x60 || (d0 < (' ' - 1) && !isspace (d0)) || + d1 > 0x60 || (d1 < (' ' - 1) && !isspace (d1))) { + t->slen = otlen; + goto bl; + } + c0 = c1 = 0; + } + outoctet[0] = (unsigned char) ((c0 << 2) | ((unsigned) c1 >> 4)); + if (t->slen+1 >= llen) { + if (0 > bconchar (t, (char) outoctet[0])) return -__LINE__; + break; + } + if ((unsigned) c2 >= 0x40) { if (!ret) ret = -__LINE__; + if (d2 > 0x60 || (d2 < (' ' - 1) && !isspace (d2))) { + t->slen = otlen; + goto bl; + } + c2 = 0; + } + outoctet[1] = (unsigned char) ((c1 << 4) | ((unsigned) c2 >> 2)); + if (t->slen+2 >= llen) { + if (0 > bcatblk (t, outoctet, 2)) return -__LINE__; + break; + } + if ((unsigned) c3 >= 0x40) { if (!ret) ret = -__LINE__; + if (d3 > 0x60 || (d3 < (' ' - 1) && !isspace (d3))) { + t->slen = otlen; + goto bl; + } + c3 = 0; + } + outoctet[2] = (unsigned char) ((c2 << 6) | ((unsigned) c3)); + if (0 > bcatblk (t, outoctet, 3)) return -__LINE__; + } + if (t->slen < llen) { if (0 == ret) ret = -__LINE__; + t->slen = otlen; + } + bl:; + if (ret && io->badlines) { + (*io->badlines)++; + return 0; + } + return ret; +} + +/* bstring bUuDecodeEx (const_bstring src, int * badlines) + * + * Performs a UUDecode of a block of data. If there are errors in the + * decoding, they are counted up and returned in "badlines", if badlines is + * not NULL. It is assumed that the "begin" and "end" lines have already + * been stripped off. The potential security problem of writing the + * filename in the begin line is something that is beyond the scope of a + * portable library. + */ + +#ifdef _MSC_VER +#pragma warning(disable:4204) +#endif + +bstring bUuDecodeEx (const_bstring src, int * badlines) { +struct tagbstring t; +struct bStream * s; +struct bStream * d; +bstring b; + + if (!src) return NULL; + t = *src; /* Short lifetime alias to header of src */ + s = bsFromBstrRef (&t); /* t is undefined after this */ + if (!s) return NULL; + d = bsUuDecode (s, badlines); + b = bfromcstralloc (256, ""); + if (NULL == b || 0 > bsread (b, d, INT_MAX)) { + bdestroy (b); + bsclose (d); + bsclose (s); + return NULL; + } + return b; +} + +struct bsUuCtx { + struct bUuInOut io; + struct bStream * sInp; +}; + +static size_t bsUuDecodePart (void *buff, size_t elsize, size_t nelem, void *parm) { +static struct tagbstring eol = bsStatic ("\r\n"); +struct bsUuCtx * luuCtx = (struct bsUuCtx *) parm; +size_t tsz; +int l, lret; + + if (NULL == buff || NULL == parm) return 0; + tsz = elsize * nelem; + + CheckInternalBuffer:; + /* If internal buffer has sufficient data, just output it */ + if (((size_t) luuCtx->io.dst->slen) > tsz) { + memcpy (buff, luuCtx->io.dst->data, tsz); + bdelete (luuCtx->io.dst, 0, (int) tsz); + return nelem; + } + + DecodeMore:; + if (0 <= (l = binchr (luuCtx->io.src, 0, &eol))) { + int ol = 0; + struct tagbstring t; + bstring s = luuCtx->io.src; + luuCtx->io.src = &t; + + do { + if (l > ol) { + bmid2tbstr (t, s, ol, l - ol); + lret = bUuDecLine (&luuCtx->io, 0, t.slen); + if (0 > lret) { + luuCtx->io.src = s; + goto Done; + } + } + ol = l + 1; + if (((size_t) luuCtx->io.dst->slen) > tsz) break; + l = binchr (s, ol, &eol); + } while (BSTR_ERR != l); + bdelete (s, 0, ol); + luuCtx->io.src = s; + goto CheckInternalBuffer; + } + + if (BSTR_ERR != bsreada (luuCtx->io.src, luuCtx->sInp, bsbufflength (luuCtx->sInp, BSTR_BS_BUFF_LENGTH_GET))) { + goto DecodeMore; + } + + bUuDecLine (&luuCtx->io, 0, luuCtx->io.src->slen); + + Done:; + /* Output any lingering data that has been translated */ + if (((size_t) luuCtx->io.dst->slen) > 0) { + if (((size_t) luuCtx->io.dst->slen) > tsz) goto CheckInternalBuffer; + memcpy (buff, luuCtx->io.dst->data, luuCtx->io.dst->slen); + tsz = luuCtx->io.dst->slen / elsize; + luuCtx->io.dst->slen = 0; + if (tsz > 0) return tsz; + } + + /* Deallocate once EOF becomes triggered */ + bdestroy (luuCtx->io.dst); + bdestroy (luuCtx->io.src); + free (luuCtx); + return 0; +} + +/* bStream * bsUuDecode (struct bStream * sInp, int * badlines) + * + * Creates a bStream which performs the UUDecode of an an input stream. If + * there are errors in the decoding, they are counted up and returned in + * "badlines", if badlines is not NULL. It is assumed that the "begin" and + * "end" lines have already been stripped off. The potential security + * problem of writing the filename in the begin line is something that is + * beyond the scope of a portable library. + */ + +struct bStream * bsUuDecode (struct bStream * sInp, int * badlines) { +struct bsUuCtx * luuCtx = (struct bsUuCtx *) malloc (sizeof (struct bsUuCtx)); +struct bStream * sOut; + + if (NULL == luuCtx) return NULL; + + luuCtx->io.src = bfromcstr (""); + luuCtx->io.dst = bfromcstr (""); + if (NULL == luuCtx->io.dst || NULL == luuCtx->io.src) { + CleanUpFailureToAllocate:; + bdestroy (luuCtx->io.dst); + bdestroy (luuCtx->io.src); + free (luuCtx); + return NULL; + } + luuCtx->io.badlines = badlines; + if (badlines) *badlines = 0; + + luuCtx->sInp = sInp; + + sOut = bsopen ((bNread) bsUuDecodePart, luuCtx); + if (NULL == sOut) goto CleanUpFailureToAllocate; + return sOut; +} + +#define UU_ENCODE_BYTE(b) (char) (((b) == 0) ? '`' : ((b) + ' ')) + +/* bstring bUuEncode (const_bstring src) + * + * Performs a UUEncode of a block of data. The "begin" and "end" lines are + * not appended. + */ +bstring bUuEncode (const_bstring src) { +bstring out; +int i, j, jm; +unsigned int c0, c1, c2; + if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; + if ((out = bfromcstr ("")) == NULL) return NULL; + for (i=0; i < src->slen; i += UU_MAX_LINELEN) { + if ((jm = i + UU_MAX_LINELEN) > src->slen) jm = src->slen; + if (bconchar (out, UU_ENCODE_BYTE (jm - i)) < 0) { + bstrFree (out); + break; + } + for (j = i; j < jm; j += 3) { + c0 = (unsigned int) bchar (src, j ); + c1 = (unsigned int) bchar (src, j + 1); + c2 = (unsigned int) bchar (src, j + 2); + if (bconchar (out, UU_ENCODE_BYTE ( (c0 & 0xFC) >> 2)) < 0 || + bconchar (out, UU_ENCODE_BYTE (((c0 & 0x03) << 4) | ((c1 & 0xF0) >> 4))) < 0 || + bconchar (out, UU_ENCODE_BYTE (((c1 & 0x0F) << 2) | ((c2 & 0xC0) >> 6))) < 0 || + bconchar (out, UU_ENCODE_BYTE ( (c2 & 0x3F))) < 0) { + bstrFree (out); + goto End; + } + } + if (bconchar (out, (char) '\r') < 0 || bconchar (out, (char) '\n') < 0) { + bstrFree (out); + break; + } + } + End:; + return out; +} + +/* bstring bYEncode (const_bstring src) + * + * Performs a YEncode of a block of data. No header or tail info is + * appended. See: http://www.yenc.org/whatis.htm and + * http://www.yenc.org/yenc-draft.1.3.txt + */ +bstring bYEncode (const_bstring src) { +int i; +bstring out; +unsigned char c; + + if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; + if ((out = bfromcstr ("")) == NULL) return NULL; + for (i=0; i < src->slen; i++) { + c = (unsigned char)(src->data[i] + 42); + if (c == '=' || c == '\0' || c == '\r' || c == '\n') { + if (0 > bconchar (out, (char) '=')) { + bdestroy (out); + return NULL; + } + c += (unsigned char) 64; + } + if (0 > bconchar (out, c)) { + bdestroy (out); + return NULL; + } + } + return out; +} + +/* bstring bYDecode (const_bstring src) + * + * Performs a YDecode of a block of data. See: + * http://www.yenc.org/whatis.htm and http://www.yenc.org/yenc-draft.1.3.txt + */ +#define MAX_OB_LEN (64) + +bstring bYDecode (const_bstring src) { +int i; +bstring out; +unsigned char c; +unsigned char octetbuff[MAX_OB_LEN]; +int obl; + + if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; + if ((out = bfromcstr ("")) == NULL) return NULL; + + obl = 0; + + for (i=0; i < src->slen; i++) { + if ('=' == (c = src->data[i])) { /* The = escape mode */ + i++; + if (i >= src->slen) { + bdestroy (out); + return NULL; + } + c = (unsigned char) (src->data[i] - 64); + } else { + if ('\0' == c) { + bdestroy (out); + return NULL; + } + + /* Extraneous CR/LFs are to be ignored. */ + if (c == '\r' || c == '\n') continue; + } + + octetbuff[obl] = (unsigned char) ((int) c - 42); + obl++; + + if (obl >= MAX_OB_LEN) { + if (0 > bcatblk (out, octetbuff, obl)) { + bdestroy (out); + return NULL; + } + obl = 0; + } + } + + if (0 > bcatblk (out, octetbuff, obl)) { + bdestroy (out); + out = NULL; + } + return out; +} + +/* bstring bStrfTime (const char * fmt, const struct tm * timeptr) + * + * Takes a format string that is compatible with strftime and a struct tm + * pointer, formats the time according to the format string and outputs + * the bstring as a result. Note that if there is an early generation of a + * '\0' character, the bstring will be truncated to this end point. + */ +bstring bStrfTime (const char * fmt, const struct tm * timeptr) { +#if defined (__TURBOC__) && !defined (__BORLANDC__) +static struct tagbstring ns = bsStatic ("bStrfTime Not supported"); + fmt = fmt; + timeptr = timeptr; + return &ns; +#else +bstring buff; +int n; +size_t r; + + if (fmt == NULL) return NULL; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "strftime" call on increasing + potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < 16) n = 16; + buff = bfromcstralloc (n+2, ""); + + for (;;) { + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return NULL; + } + + r = strftime ((char *) buff->data, n + 1, fmt, timeptr); + + if (r > 0) { + buff->slen = (int) r; + break; + } + + n += n; + } + + return buff; +#endif +} + +/* int bSetCstrChar (bstring a, int pos, char c) + * + * Sets the character at position pos to the character c in the bstring a. + * If the character c is NUL ('\0') then the string is truncated at this + * point. Note: this does not enable any other '\0' character in the bstring + * as terminator indicator for the string. pos must be in the position + * between 0 and b->slen inclusive, otherwise BSTR_ERR will be returned. + */ +int bSetCstrChar (bstring b, int pos, char c) { + if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) + return BSTR_ERR; + if (pos < 0 || pos > b->slen) return BSTR_ERR; + + if (pos == b->slen) { + if ('\0' != c) return bconchar (b, c); + return 0; + } + + b->data[pos] = (unsigned char) c; + if ('\0' == c) b->slen = pos; + + return 0; +} + +/* int bSetChar (bstring b, int pos, char c) + * + * Sets the character at position pos to the character c in the bstring a. + * The string is not truncated if the character c is NUL ('\0'). pos must + * be in the position between 0 and b->slen inclusive, otherwise BSTR_ERR + * will be returned. + */ +int bSetChar (bstring b, int pos, char c) { + if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) + return BSTR_ERR; + if (pos < 0 || pos > b->slen) return BSTR_ERR; + + if (pos == b->slen) { + return bconchar (b, c); + } + + b->data[pos] = (unsigned char) c; + return 0; +} + +#define INIT_SECURE_INPUT_LENGTH (256) + +/* bstring bSecureInput (int maxlen, int termchar, + * bNgetc vgetchar, void * vgcCtx) + * + * Read input from an abstracted input interface, for a length of at most + * maxlen characters. If maxlen <= 0, then there is no length limit put + * on the input. The result is terminated early if vgetchar() return EOF + * or the user specified value termchar. + * + */ +bstring bSecureInput (int maxlen, int termchar, bNgetc vgetchar, void * vgcCtx) { +int i, m, c; +bstring b, t; + + if (!vgetchar) return NULL; + + b = bfromcstralloc (INIT_SECURE_INPUT_LENGTH, ""); + if ((c = UCHAR_MAX + 1) == termchar) c++; + + for (i=0; ; i++) { + if (termchar == c || (maxlen > 0 && i >= maxlen)) c = EOF; + else c = vgetchar (vgcCtx); + + if (EOF == c) break; + + if (i+1 >= b->mlen) { + + /* Double size, but deal with unusual case of numeric + overflows */ + + if ((m = b->mlen << 1) <= b->mlen && + (m = b->mlen + 1024) <= b->mlen && + (m = b->mlen + 16) <= b->mlen && + (m = b->mlen + 1) <= b->mlen) t = NULL; + else t = bfromcstralloc (m, ""); + + if (t) memcpy (t->data, b->data, i); + bSecureDestroy (b); /* Cleanse previous buffer */ + b = t; + if (!b) return b; + } + + b->data[i] = (unsigned char) c; + } + + b->slen = i; + b->data[i] = (unsigned char) '\0'; + return b; +} + +#define BWS_BUFF_SZ (1024) + +struct bwriteStream { + bstring buff; /* Buffer for underwrites */ + void * parm; /* The stream handle for core stream */ + bNwrite writeFn; /* fwrite work-a-like fnptr for core stream */ + int isEOF; /* track stream's EOF state */ + int minBuffSz; +}; + +/* struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) + * + * Wrap a given open stream (described by a fwrite work-a-like function + * pointer and stream handle) into an open bwriteStream suitable for write + * streaming functions. + */ +struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) { +struct bwriteStream * ws; + + if (NULL == writeFn) return NULL; + ws = (struct bwriteStream *) malloc (sizeof (struct bwriteStream)); + if (ws) { + if (NULL == (ws->buff = bfromcstr (""))) { + free (ws); + ws = NULL; + } else { + ws->parm = parm; + ws->writeFn = writeFn; + ws->isEOF = 0; + ws->minBuffSz = BWS_BUFF_SZ; + } + } + return ws; +} + +#define internal_bwswriteout(ws,b) { \ + if ((b)->slen > 0) { \ + if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \ + ws->isEOF = 1; \ + return BSTR_ERR; \ + } \ + } \ +} + +/* int bwsWriteFlush (struct bwriteStream * ws) + * + * Force any pending data to be written to the core stream. + */ +int bwsWriteFlush (struct bwriteStream * ws) { + if (NULL == ws || ws->isEOF || 0 >= ws->minBuffSz || + NULL == ws->writeFn || NULL == ws->buff) return BSTR_ERR; + internal_bwswriteout (ws, ws->buff); + ws->buff->slen = 0; + return 0; +} + +/* int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) + * + * Send a bstring to a bwriteStream. If the stream is at EOF BSTR_ERR is + * returned. Note that there is no deterministic way to determine the exact + * cut off point where the core stream stopped accepting data. + */ +int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) { +struct tagbstring t; +int l; + + if (NULL == ws || NULL == b || NULL == ws->buff || + ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn) + return BSTR_ERR; + + /* Buffer prepacking optimization */ + if (b->slen > 0 && ws->buff->mlen - ws->buff->slen > b->slen) { + static struct tagbstring empty = bsStatic (""); + if (0 > bconcat (ws->buff, b)) return BSTR_ERR; + return bwsWriteBstr (ws, &empty); + } + + if (0 > (l = ws->minBuffSz - ws->buff->slen)) { + internal_bwswriteout (ws, ws->buff); + ws->buff->slen = 0; + l = ws->minBuffSz; + } + + if (b->slen < l) return bconcat (ws->buff, b); + + if (0 > bcatblk (ws->buff, b->data, l)) return BSTR_ERR; + internal_bwswriteout (ws, ws->buff); + ws->buff->slen = 0; + + bmid2tbstr (t, (bstring) b, l, b->slen); + + if (t.slen >= ws->minBuffSz) { + internal_bwswriteout (ws, &t); + return 0; + } + + return bassign (ws->buff, &t); +} + +/* int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) + * + * Send a block of data a bwriteStream. If the stream is at EOF BSTR_ERR is + * returned. + */ +int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) { +struct tagbstring t; + if (NULL == blk || len < 0) return BSTR_ERR; + blk2tbstr (t, blk, len); + return bwsWriteBstr (ws, &t); +} + +/* int bwsIsEOF (const struct bwriteStream * ws) + * + * Returns 0 if the stream is currently writable, 1 if the core stream has + * responded by not accepting the previous attempted write. + */ +int bwsIsEOF (const struct bwriteStream * ws) { + if (NULL == ws || NULL == ws->buff || 0 > ws->minBuffSz || + NULL == ws->writeFn) return BSTR_ERR; + return ws->isEOF; +} + +/* int bwsBuffLength (struct bwriteStream * ws, int sz) + * + * Set the length of the buffer used by the bwsStream. If sz is zero, the + * length is not set. This function returns with the previous length. + */ +int bwsBuffLength (struct bwriteStream * ws, int sz) { +int oldSz; + if (ws == NULL || sz < 0) return BSTR_ERR; + oldSz = ws->minBuffSz; + if (sz > 0) ws->minBuffSz = sz; + return oldSz; +} + +/* void * bwsClose (struct bwriteStream * s) + * + * Close the bwriteStream, and return the handle to the stream that was + * originally used to open the given stream. Note that even if the stream + * is at EOF it still needs to be closed with a call to bwsClose. + */ +void * bwsClose (struct bwriteStream * ws) { +void * parm; + if (NULL == ws || NULL == ws->buff || 0 >= ws->minBuffSz || + NULL == ws->writeFn) return NULL; + bwsWriteFlush (ws); + parm = ws->parm; + ws->parm = NULL; + ws->minBuffSz = -1; + ws->writeFn = NULL; + bstrFree (ws->buff); + free (ws); + return parm; +} + diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.h b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.h new file mode 100644 index 0000000000..e10c6e1a68 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstraux.h @@ -0,0 +1,113 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstraux.h + * + * This file is not a necessary part of the core bstring library itself, but + * is just an auxilliary module which includes miscellaneous or trivial + * functions. + */ + +#ifndef BSTRAUX_INCLUDE +#define BSTRAUX_INCLUDE + +#include +#include "bstrlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Safety mechanisms */ +#define bstrDeclare(b) bstring (b) = NULL; +#define bstrFree(b) {if ((b) != NULL && (b)->slen >= 0 && (b)->mlen >= (b)->slen) { bdestroy (b); (b) = NULL; }} + +/* Backward compatibilty with previous versions of Bstrlib */ +#define bAssign(a,b) ((bassign)((a), (b))) +#define bSubs(b,pos,len,a,c) ((breplace)((b),(pos),(len),(a),(unsigned char)(c))) +#define bStrchr(b,c) ((bstrchr)((b), (c))) +#define bStrchrFast(b,c) ((bstrchr)((b), (c))) +#define bCatCstr(b,s) ((bcatcstr)((b), (s))) +#define bCatBlk(b,s,len) ((bcatblk)((b),(s),(len))) +#define bCatStatic(b,s) bCatBlk ((b), ("" s ""), sizeof (s) - 1) +#define bTrunc(b,n) ((btrunc)((b), (n))) +#define bReplaceAll(b,find,repl,pos) ((bfindreplace)((b),(find),(repl),(pos))) +#define bUppercase(b) ((btoupper)(b)) +#define bLowercase(b) ((btolower)(b)) +#define bCaselessCmp(a,b) ((bstricmp)((a), (b))) +#define bCaselessNCmp(a,b,n) ((bstrnicmp)((a), (b), (n))) +#define bBase64Decode(b) (bBase64DecodeEx ((b), NULL)) +#define bUuDecode(b) (bUuDecodeEx ((b), NULL)) + +/* Unusual functions */ +extern struct bStream * bsFromBstr (const_bstring b); +extern bstring bTail (bstring b, int n); +extern bstring bHead (bstring b, int n); +extern int bSetCstrChar (bstring a, int pos, char c); +extern int bSetChar (bstring b, int pos, char c); +extern int bFill (bstring a, char c, int len); +extern int bReplicate (bstring b, int n); +extern int bReverse (bstring b); +extern int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill); +extern bstring bStrfTime (const char * fmt, const struct tm * timeptr); +#define bAscTime(t) (bStrfTime ("%c\n", (t))) +#define bCTime(t) ((t) ? bAscTime (localtime (t)) : NULL) + +/* Spacing formatting */ +extern int bJustifyLeft (bstring b, int space); +extern int bJustifyRight (bstring b, int width, int space); +extern int bJustifyMargin (bstring b, int width, int space); +extern int bJustifyCenter (bstring b, int width, int space); + +/* Esoteric standards specific functions */ +extern char * bStr2NetStr (const_bstring b); +extern bstring bNetStr2Bstr (const char * buf); +extern bstring bBase64Encode (const_bstring b); +extern bstring bBase64DecodeEx (const_bstring b, int * boolTruncError); +extern struct bStream * bsUuDecode (struct bStream * sInp, int * badlines); +extern bstring bUuDecodeEx (const_bstring src, int * badlines); +extern bstring bUuEncode (const_bstring src); +extern bstring bYEncode (const_bstring src); +extern bstring bYDecode (const_bstring src); + +/* Writable stream */ +typedef int (* bNwrite) (const void * buf, size_t elsize, size_t nelem, void * parm); + +struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm); +int bwsWriteBstr (struct bwriteStream * stream, const_bstring b); +int bwsWriteBlk (struct bwriteStream * stream, void * blk, int len); +int bwsWriteFlush (struct bwriteStream * stream); +int bwsIsEOF (const struct bwriteStream * stream); +int bwsBuffLength (struct bwriteStream * stream, int sz); +void * bwsClose (struct bwriteStream * stream); + +/* Security functions */ +#define bSecureDestroy(b) { \ +bstring bstr__tmp = (b); \ + if (bstr__tmp && bstr__tmp->mlen > 0 && bstr__tmp->data) { \ + (void) memset (bstr__tmp->data, 0, (size_t) bstr__tmp->mlen); \ + bdestroy (bstr__tmp); \ + } \ +} +#define bSecureWriteProtect(t) { \ + if ((t).mlen >= 0) { \ + if ((t).mlen > (t).slen)) { \ + (void) memset ((t).data + (t).slen, 0, (size_t) (t).mlen - (t).slen); \ + } \ + (t).mlen = -1; \ + } \ +} +extern bstring bSecureInput (int maxlen, int termchar, + bNgetc vgetchar, void * vgcCtx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.c b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.c new file mode 100644 index 0000000000..7c233454ba --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.c @@ -0,0 +1,2976 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstrlib.c + * + * This file is the core module for implementing the bstring functions. + */ + +#include +#include +#include +#include +#include +#include +#include "bstrlib.h" +#include "../internal_includes/hlslcc_malloc.h" + +/* Optionally include a mechanism for debugging memory */ + +#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG) +#include "memdbg.h" +#endif + +#ifndef bstr__alloc +#define bstr__alloc(x) malloc (x) +#endif + +#ifndef bstr__free +#define bstr__free(p) free (p) +#endif + +#ifndef bstr__realloc +#define bstr__realloc(p,x) realloc ((p), (x)) +#endif + +#ifndef bstr__memcpy +#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l)) +#endif + +#ifndef bstr__memmove +#define bstr__memmove(d,s,l) memmove ((d), (s), (l)) +#endif + +#ifndef bstr__memset +#define bstr__memset(d,c,l) memset ((d), (c), (l)) +#endif + +#ifndef bstr__memcmp +#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l)) +#endif + +#ifndef bstr__memchr +#define bstr__memchr(s,c,l) memchr ((s), (c), (l)) +#endif + +/* Just a length safe wrapper for memmove. */ + +#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); } + +/* Compute the snapped size for a given requested size. By snapping to powers + of 2 like this, repeated reallocations are avoided. */ +static int snapUpSize (int i) { + if (i < 8) { + i = 8; + } else { + unsigned int j; + j = (unsigned int) i; + + j |= (j >> 1); + j |= (j >> 2); + j |= (j >> 4); + j |= (j >> 8); /* Ok, since int >= 16 bits */ +#if (UINT_MAX != 0xffff) + j |= (j >> 16); /* For 32 bit int systems */ +#if (UINT_MAX > 0xffffffffUL) + j |= (j >> 32); /* For 64 bit int systems */ +#endif +#endif + /* Least power of two greater than i */ + j++; + if ((int) j >= i) i = (int) j; + } + return i; +} + +/* int balloc (bstring b, int len) + * + * Increase the size of the memory backing the bstring b to at least len. + */ +int balloc (bstring b, int olen) { + int len; + if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || + b->mlen < b->slen || olen <= 0) { + return BSTR_ERR; + } + + if (olen >= b->mlen) { + unsigned char * x; + + if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK; + + /* Assume probability of a non-moving realloc is 0.125 */ + if (7 * b->mlen < 8 * b->slen) { + + /* If slen is close to mlen in size then use realloc to reduce + the memory defragmentation */ + + reallocStrategy:; + + x = (unsigned char *) bstr__realloc (b->data, (size_t) len); + if (x == NULL) { + + /* Since we failed, try allocating the tighest possible + allocation */ + + if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) { + return BSTR_ERR; + } + } + } else { + + /* If slen is not close to mlen then avoid the penalty of copying + the extra bytes that are allocated, but not considered part of + the string */ + + if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) { + + /* Perhaps there is no available memory for the two + allocations to be in memory at once */ + + goto reallocStrategy; + + } else { + if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen); + bstr__free (b->data); + } + } + b->data = x; + b->mlen = len; + b->data[b->slen] = (unsigned char) '\0'; + } + + return BSTR_OK; +} + +/* int ballocmin (bstring b, int len) + * + * Set the size of the memory backing the bstring b to len or b->slen+1, + * whichever is larger. Note that repeated use of this function can degrade + * performance. + */ +int ballocmin (bstring b, int len) { + unsigned char * s; + + if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 || + b->mlen < b->slen || len <= 0) { + return BSTR_ERR; + } + + if (len < b->slen + 1) len = b->slen + 1; + + if (len != b->mlen) { + s = (unsigned char *) bstr__realloc (b->data, (size_t) len); + if (NULL == s) return BSTR_ERR; + s[b->slen] = (unsigned char) '\0'; + b->data = s; + b->mlen = len; + } + + return BSTR_OK; +} + +/* bstring bfromcstr (const char * str) + * + * Create a bstring which contains the contents of the '\0' terminated char * + * buffer str. + */ +bstring bfromcstr (const char * str) { +bstring b; +int i; +size_t j; + + if (str == NULL) return NULL; + j = (strlen) (str); + i = snapUpSize ((int) (j + (2 - (j != 0)))); + if (i <= (int) j) return NULL; + + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (NULL == b) return NULL; + b->slen = (int) j; + if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) { + bstr__free (b); + return NULL; + } + + bstr__memcpy (b->data, str, j+1); + return b; +} + +/* bstring bfromcstralloc (int mlen, const char * str) + * + * Create a bstring which contains the contents of the '\0' terminated char * + * buffer str. The memory buffer backing the string is at least len + * characters in length. + */ +bstring bfromcstralloc (int mlen, const char * str) { +bstring b; +int i; +size_t j; + + if (str == NULL) return NULL; + j = (strlen) (str); + i = snapUpSize ((int) (j + (2 - (j != 0)))); + if (i <= (int) j) return NULL; + + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (b == NULL) return NULL; + b->slen = (int) j; + if (i < mlen) i = mlen; + + if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) { + bstr__free (b); + return NULL; + } + + bstr__memcpy (b->data, str, j+1); + return b; +} + +/* bstring blk2bstr (const void * blk, int len) + * + * Create a bstring which contains the content of the block blk of length + * len. + */ +bstring blk2bstr (const void * blk, int len) { +bstring b; +int i; + + if (blk == NULL || len < 0) return NULL; + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (b == NULL) return NULL; + b->slen = len; + + i = len + (2 - (len != 0)); + i = snapUpSize (i); + + b->mlen = i; + + b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen); + if (b->data == NULL) { + bstr__free (b); + return NULL; + } + + if (len > 0) bstr__memcpy (b->data, blk, (size_t) len); + b->data[len] = (unsigned char) '\0'; + + return b; +} + +/* char * bstr2cstr (const_bstring s, char z) + * + * Create a '\0' terminated char * buffer which is equal to the contents of + * the bstring s, except that any contained '\0' characters are converted + * to the character in z. This returned value should be freed with a + * bcstrfree () call, by the calling application. + */ +char * bstr2cstr (const_bstring b, char z) { +int i, l; +char * r; + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + l = b->slen; + r = (char *) bstr__alloc ((size_t) (l + 1)); + if (r == NULL) return r; + + for (i=0; i < l; i ++) { + r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i])); + } + + r[l] = (unsigned char) '\0'; + + return r; +} + +/* int bcstrfree (char * s) + * + * Frees a C-string generated by bstr2cstr (). This is normally unnecessary + * since it just wraps a call to bstr__free (), however, if bstr__alloc () + * and bstr__free () have been redefined as a macros within the bstrlib + * module (via defining them in memdbg.h after defining + * BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std + * library functions, then this allows a correct way of freeing the memory + * that allows higher level code to be independent from these macro + * redefinitions. + */ +int bcstrfree (char * s) { + if (s) { + bstr__free (s); + return BSTR_OK; + } + return BSTR_ERR; +} + +/* int bconcat (bstring b0, const_bstring b1) + * + * Concatenate the bstring b1 to the bstring b0. + */ +int bconcat (bstring b0, const_bstring b1) { +int len, d; +bstring aux = (bstring) b1; + + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR; + + d = b0->slen; + len = b1->slen; + if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR; + + if (b0->mlen <= d + len + 1) { + ptrdiff_t pd = b1->data - b0->data; + if (0 <= pd && pd < b0->mlen) { + if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; + } + if (balloc (b0, d + len + 1) != BSTR_OK) { + if (aux != b1) bdestroy (aux); + return BSTR_ERR; + } + } + + bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len); + b0->data[d + len] = (unsigned char) '\0'; + b0->slen = d + len; + if (aux != b1) bdestroy (aux); + return BSTR_OK; +} + +/* int bconchar (bstring b, char c) +/ * + * Concatenate the single character c to the bstring b. + */ +int bconchar (bstring b, char c) { +int d; + + if (b == NULL) return BSTR_ERR; + d = b->slen; + if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; + b->data[d] = (unsigned char) c; + b->data[d + 1] = (unsigned char) '\0'; + b->slen++; + return BSTR_OK; +} + +/* int bcatcstr (bstring b, const char * s) + * + * Concatenate a char * string to a bstring. + */ +int bcatcstr (bstring b, const char * s) { +char * d; +int i, l; + + if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen + || b->mlen <= 0 || s == NULL) return BSTR_ERR; + + /* Optimistically concatenate directly */ + l = b->mlen - b->slen; + d = (char *) &b->data[b->slen]; + for (i=0; i < l; i++) { + if ((*d++ = *s++) == '\0') { + b->slen += i; + return BSTR_OK; + } + } + b->slen += i; + + /* Need to explicitely resize and concatenate tail */ + return bcatblk (b, (const void *) s, (int) strlen (s)); +} + +/* int bcatblk (bstring b, const void * s, int len) + * + * Concatenate a fixed length buffer to a bstring. + */ +int bcatblk (bstring b, const void * s, int len) { +int nl; + + if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen + || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR; + + if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */ + if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR; + + bBlockCopy (&b->data[b->slen], s, (size_t) len); + b->slen = nl; + b->data[nl] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* bstring bstrcpy (const_bstring b) + * + * Create a copy of the bstring b. + */ +bstring bstrcpy (const_bstring b) { +bstring b0; +int i,j; + + /* Attempted to copy an invalid string? */ + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + + b0 = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (b0 == NULL) { + /* Unable to allocate memory for string header */ + return NULL; + } + + i = b->slen; + j = snapUpSize (i + 1); + + b0->data = (unsigned char *) bstr__alloc (j); + if (b0->data == NULL) { + j = i + 1; + b0->data = (unsigned char *) bstr__alloc (j); + if (b0->data == NULL) { + /* Unable to allocate memory for string data */ + bstr__free (b0); + return NULL; + } + } + + b0->mlen = j; + b0->slen = i; + + if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i); + b0->data[b0->slen] = (unsigned char) '\0'; + + return b0; +} + +/* int bassign (bstring a, const_bstring b) + * + * Overwrite the string a with the contents of string b. + */ +int bassign (bstring a, const_bstring b) { + if (b == NULL || b->data == NULL || b->slen < 0) + return BSTR_ERR; + if (b->slen != 0) { + if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR; + bstr__memmove (a->data, b->data, b->slen); + } else { + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0) + return BSTR_ERR; + } + a->data[b->slen] = (unsigned char) '\0'; + a->slen = b->slen; + return BSTR_OK; +} + +/* int bassignmidstr (bstring a, const_bstring b, int left, int len) + * + * Overwrite the string a with the middle of contents of string b + * starting from position left and running for a length len. left and + * len are clamped to the ends of b as with the function bmidstr. + */ +int bassignmidstr (bstring a, const_bstring b, int left, int len) { + if (b == NULL || b->data == NULL || b->slen < 0) + return BSTR_ERR; + + if (left < 0) { + len += left; + left = 0; + } + + if (len > b->slen - left) len = b->slen - left; + + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0) + return BSTR_ERR; + + if (len > 0) { + if (balloc (a, len) != BSTR_OK) return BSTR_ERR; + bstr__memmove (a->data, b->data + left, len); + a->slen = len; + } else { + a->slen = 0; + } + a->data[a->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bassigncstr (bstring a, const char * str) + * + * Overwrite the string a with the contents of char * string str. Note that + * the bstring a must be a well defined and writable bstring. If an error + * occurs BSTR_ERR is returned however a may be partially overwritten. + */ +int bassigncstr (bstring a, const char * str) { +int i; +size_t len; + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0 || NULL == str) + return BSTR_ERR; + + for (i=0; i < a->mlen; i++) { + if ('\0' == (a->data[i] = str[i])) { + a->slen = i; + return BSTR_OK; + } + } + + a->slen = i; + len = strlen (str + i); + if (len > INT_MAX || i + len + 1 > INT_MAX || + 0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR; + bBlockCopy (a->data + i, str + i, (size_t) len + 1); + a->slen += (int) len; + return BSTR_OK; +} + +/* int bassignblk (bstring a, const void * s, int len) + * + * Overwrite the string a with the contents of the block (s, len). Note that + * the bstring a must be a well defined and writable bstring. If an error + * occurs BSTR_ERR is returned and a is not overwritten. + */ +int bassignblk (bstring a, const void * s, int len) { + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1) + return BSTR_ERR; + if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR; + bBlockCopy (a->data, s, (size_t) len); + a->data[len] = (unsigned char) '\0'; + a->slen = len; + return BSTR_OK; +} + +/* int btrunc (bstring b, int n) + * + * Truncate the bstring to at most n characters. + */ +int btrunc (bstring b, int n) { + if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + if (b->slen > n) { + b->slen = n; + b->data[n] = (unsigned char) '\0'; + } + return BSTR_OK; +} + +#define upcase(c) (toupper ((unsigned char) c)) +#define downcase(c) (tolower ((unsigned char) c)) +#define wspace(c) (isspace ((unsigned char) c)) + +/* int btoupper (bstring b) + * + * Convert contents of bstring to upper case. + */ +int btoupper (bstring b) { +int i, len; + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + for (i=0, len = b->slen; i < len; i++) { + b->data[i] = (unsigned char) upcase (b->data[i]); + } + return BSTR_OK; +} + +/* int btolower (bstring b) + * + * Convert contents of bstring to lower case. + */ +int btolower (bstring b) { +int i, len; + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + for (i=0, len = b->slen; i < len; i++) { + b->data[i] = (unsigned char) downcase (b->data[i]); + } + return BSTR_OK; +} + +/* int bstricmp (const_bstring b0, const_bstring b1) + * + * Compare two strings without differentiating between case. The return + * value is the difference of the values of the characters where the two + * strings first differ after lower case transformation, otherwise 0 is + * returned indicating that the strings are equal. If the lengths are + * different, then a difference from 0 is given, but if the first extra + * character is '\0', then it is taken to be the value UCHAR_MAX+1. + */ +int bstricmp (const_bstring b0, const_bstring b1) { +int i, v, n; + + if (bdata (b0) == NULL || b0->slen < 0 || + bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN; + if ((n = b0->slen) > b1->slen) n = b1->slen; + else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK; + + for (i = 0; i < n; i ++) { + v = (char) downcase (b0->data[i]) + - (char) downcase (b1->data[i]); + if (0 != v) return v; + } + + if (b0->slen > n) { + v = (char) downcase (b0->data[n]); + if (v) return v; + return UCHAR_MAX + 1; + } + if (b1->slen > n) { + v = - (char) downcase (b1->data[n]); + if (v) return v; + return - (int) (UCHAR_MAX + 1); + } + return BSTR_OK; +} + +/* int bstrnicmp (const_bstring b0, const_bstring b1, int n) + * + * Compare two strings without differentiating between case for at most n + * characters. If the position where the two strings first differ is + * before the nth position, the return value is the difference of the values + * of the characters, otherwise 0 is returned. If the lengths are different + * and less than n characters, then a difference from 0 is given, but if the + * first extra character is '\0', then it is taken to be the value + * UCHAR_MAX+1. + */ +int bstrnicmp (const_bstring b0, const_bstring b1, int n) { +int i, v, m; + + if (bdata (b0) == NULL || b0->slen < 0 || + bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN; + m = n; + if (m > b0->slen) m = b0->slen; + if (m > b1->slen) m = b1->slen; + + if (b0->data != b1->data) { + for (i = 0; i < m; i ++) { + v = (char) downcase (b0->data[i]); + v -= (char) downcase (b1->data[i]); + if (v != 0) return b0->data[i] - b1->data[i]; + } + } + + if (n == m || b0->slen == b1->slen) return BSTR_OK; + + if (b0->slen > m) { + v = (char) downcase (b0->data[m]); + if (v) return v; + return UCHAR_MAX + 1; + } + + v = - (char) downcase (b1->data[m]); + if (v) return v; + return - (int) (UCHAR_MAX + 1); +} + +/* int biseqcaseless (const_bstring b0, const_bstring b1) + * + * Compare two strings for equality without differentiating between case. + * If the strings differ other than in case, 0 is returned, if the strings + * are the same, 1 is returned, if there is an error, -1 is returned. If + * the length of the strings are different, this function is O(1). '\0' + * termination characters are not treated in any special way. + */ +int biseqcaseless (const_bstring b0, const_bstring b1) { +int i, n; + + if (bdata (b0) == NULL || b0->slen < 0 || + bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR; + if (b0->slen != b1->slen) return BSTR_OK; + if (b0->data == b1->data || b0->slen == 0) return 1; + for (i=0, n=b0->slen; i < n; i++) { + if (b0->data[i] != b1->data[i]) { + unsigned char c = (unsigned char) downcase (b0->data[i]); + if (c != (unsigned char) downcase (b1->data[i])) return 0; + } + } + return 1; +} + +/* int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) + * + * Compare beginning of string b0 with a block of memory of length len + * without differentiating between case for equality. If the beginning of b0 + * differs from the memory block other than in case (or if b0 is too short), + * 0 is returned, if the strings are the same, 1 is returned, if there is an + * error, -1 is returned. '\0' characters are not treated in any special + * way. + */ +int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) { +int i; + + if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) + return BSTR_ERR; + if (b0->slen < len) return BSTR_OK; + if (b0->data == (const unsigned char *) blk || len == 0) return 1; + + for (i = 0; i < len; i ++) { + if (b0->data[i] != ((const unsigned char *) blk)[i]) { + if (downcase (b0->data[i]) != + downcase (((const unsigned char *) blk)[i])) return 0; + } + } + return 1; +} + +/* + * int bltrimws (bstring b) + * + * Delete whitespace contiguous from the left end of the string. + */ +int bltrimws (bstring b) { +int i, len; + + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + + for (len = b->slen, i = 0; i < len; i++) { + if (!wspace (b->data[i])) { + return bdelete (b, 0, i); + } + } + + b->data[0] = (unsigned char) '\0'; + b->slen = 0; + return BSTR_OK; +} + +/* + * int brtrimws (bstring b) + * + * Delete whitespace contiguous from the right end of the string. + */ +int brtrimws (bstring b) { +int i; + + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + + for (i = b->slen - 1; i >= 0; i--) { + if (!wspace (b->data[i])) { + if (b->mlen > i) b->data[i+1] = (unsigned char) '\0'; + b->slen = i + 1; + return BSTR_OK; + } + } + + b->data[0] = (unsigned char) '\0'; + b->slen = 0; + return BSTR_OK; +} + +/* + * int btrimws (bstring b) + * + * Delete whitespace contiguous from both ends of the string. + */ +int btrimws (bstring b) { +int i, j; + + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + + for (i = b->slen - 1; i >= 0; i--) { + if (!wspace (b->data[i])) { + if (b->mlen > i) b->data[i+1] = (unsigned char) '\0'; + b->slen = i + 1; + for (j = 0; wspace (b->data[j]); j++) {} + return bdelete (b, 0, j); + } + } + + b->data[0] = (unsigned char) '\0'; + b->slen = 0; + return BSTR_OK; +} + +/* int biseq (const_bstring b0, const_bstring b1) + * + * Compare the string b0 and b1. If the strings differ, 0 is returned, if + * the strings are the same, 1 is returned, if there is an error, -1 is + * returned. If the length of the strings are different, this function is + * O(1). '\0' termination characters are not treated in any special way. + */ +int biseq (const_bstring b0, const_bstring b1) { + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || + b0->slen < 0 || b1->slen < 0) return BSTR_ERR; + if (b0->slen != b1->slen) return BSTR_OK; + if (b0->data == b1->data || b0->slen == 0) return 1; + return !bstr__memcmp (b0->data, b1->data, b0->slen); +} + +/* int bisstemeqblk (const_bstring b0, const void * blk, int len) + * + * Compare beginning of string b0 with a block of memory of length len for + * equality. If the beginning of b0 differs from the memory block (or if b0 + * is too short), 0 is returned, if the strings are the same, 1 is returned, + * if there is an error, -1 is returned. '\0' characters are not treated in + * any special way. + */ +int bisstemeqblk (const_bstring b0, const void * blk, int len) { +int i; + + if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) + return BSTR_ERR; + if (b0->slen < len) return BSTR_OK; + if (b0->data == (const unsigned char *) blk || len == 0) return 1; + + for (i = 0; i < len; i ++) { + if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK; + } + return 1; +} + +/* int biseqcstr (const_bstring b, const char *s) + * + * Compare the bstring b and char * string s. The C string s must be '\0' + * terminated at exactly the length of the bstring b, and the contents + * between the two must be identical with the bstring b with no '\0' + * characters for the two contents to be considered equal. This is + * equivalent to the condition that their current contents will be always be + * equal when comparing them in the same format after converting one or the + * other. If the strings are equal 1 is returned, if they are unequal 0 is + * returned and if there is a detectable error BSTR_ERR is returned. + */ +int biseqcstr (const_bstring b, const char * s) { +int i; + if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR; + for (i=0; i < b->slen; i++) { + if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK; + } + return s[i] == '\0'; +} + +/* int biseqcstrcaseless (const_bstring b, const char *s) + * + * Compare the bstring b and char * string s. The C string s must be '\0' + * terminated at exactly the length of the bstring b, and the contents + * between the two must be identical except for case with the bstring b with + * no '\0' characters for the two contents to be considered equal. This is + * equivalent to the condition that their current contents will be always be + * equal ignoring case when comparing them in the same format after + * converting one or the other. If the strings are equal, except for case, + * 1 is returned, if they are unequal regardless of case 0 is returned and + * if there is a detectable error BSTR_ERR is returned. + */ +int biseqcstrcaseless (const_bstring b, const char * s) { +int i; + if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR; + for (i=0; i < b->slen; i++) { + if (s[i] == '\0' || + (b->data[i] != (unsigned char) s[i] && + downcase (b->data[i]) != (unsigned char) downcase (s[i]))) + return BSTR_OK; + } + return s[i] == '\0'; +} + +/* int bstrcmp (const_bstring b0, const_bstring b1) + * + * Compare the string b0 and b1. If there is an error, SHRT_MIN is returned, + * otherwise a value less than or greater than zero, indicating that the + * string pointed to by b0 is lexicographically less than or greater than + * the string pointed to by b1 is returned. If the the string lengths are + * unequal but the characters up until the length of the shorter are equal + * then a value less than, or greater than zero, indicating that the string + * pointed to by b0 is shorter or longer than the string pointed to by b1 is + * returned. 0 is returned if and only if the two strings are the same. If + * the length of the strings are different, this function is O(n). Like its + * standard C library counter part strcmp, the comparison does not proceed + * past any '\0' termination characters encountered. + */ +int bstrcmp (const_bstring b0, const_bstring b1) { +int i, v, n; + + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || + b0->slen < 0 || b1->slen < 0) return SHRT_MIN; + n = b0->slen; if (n > b1->slen) n = b1->slen; + if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0)) + return BSTR_OK; + + for (i = 0; i < n; i ++) { + v = ((char) b0->data[i]) - ((char) b1->data[i]); + if (v != 0) return v; + if (b0->data[i] == (unsigned char) '\0') return BSTR_OK; + } + + if (b0->slen > n) return 1; + if (b1->slen > n) return -1; + return BSTR_OK; +} + +/* int bstrncmp (const_bstring b0, const_bstring b1, int n) + * + * Compare the string b0 and b1 for at most n characters. If there is an + * error, SHRT_MIN is returned, otherwise a value is returned as if b0 and + * b1 were first truncated to at most n characters then bstrcmp was called + * with these new strings are paremeters. If the length of the strings are + * different, this function is O(n). Like its standard C library counter + * part strcmp, the comparison does not proceed past any '\0' termination + * characters encountered. + */ +int bstrncmp (const_bstring b0, const_bstring b1, int n) { +int i, v, m; + + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || + b0->slen < 0 || b1->slen < 0) return SHRT_MIN; + m = n; + if (m > b0->slen) m = b0->slen; + if (m > b1->slen) m = b1->slen; + + if (b0->data != b1->data) { + for (i = 0; i < m; i ++) { + v = ((char) b0->data[i]) - ((char) b1->data[i]); + if (v != 0) return v; + if (b0->data[i] == (unsigned char) '\0') return BSTR_OK; + } + } + + if (n == m || b0->slen == b1->slen) return BSTR_OK; + + if (b0->slen > m) return 1; + return -1; +} + +/* bstring bmidstr (const_bstring b, int left, int len) + * + * Create a bstring which is the substring of b starting from position left + * and running for a length len (clamped by the end of the bstring b.) If + * b is detectably invalid, then NULL is returned. The section described + * by (left, len) is clamped to the boundaries of b. + */ +bstring bmidstr (const_bstring b, int left, int len) { + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + + if (left < 0) { + len += left; + left = 0; + } + + if (len > b->slen - left) len = b->slen - left; + + if (len <= 0) return bfromcstr (""); + return blk2bstr (b->data + left, len); +} + +/* int bdelete (bstring b, int pos, int len) + * + * Removes characters from pos to pos+len-1 inclusive and shifts the tail of + * the bstring starting from pos+len to pos. len must be positive for this + * call to have any effect. The section of the string described by (pos, + * len) is clamped to boundaries of the bstring b. + */ +int bdelete (bstring b, int pos, int len) { + /* Clamp to left side of bstring */ + if (pos < 0) { + len += pos; + pos = 0; + } + + if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || + b->mlen < b->slen || b->mlen <= 0) + return BSTR_ERR; + if (len > 0 && pos < b->slen) { + if (pos + len >= b->slen) { + b->slen = pos; + } else { + bBlockCopy ((char *) (b->data + pos), + (char *) (b->data + pos + len), + b->slen - (pos+len)); + b->slen -= len; + } + b->data[b->slen] = (unsigned char) '\0'; + } + return BSTR_OK; +} + +/* int bdestroy (bstring b) + * + * Free up the bstring. Note that if b is detectably invalid or not writable + * then no action is performed and BSTR_ERR is returned. Like a freed memory + * allocation, dereferences, writes or any other action on b after it has + * been bdestroyed is undefined. + */ +int bdestroy (bstring b) { + if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen || + b->data == NULL) + return BSTR_ERR; + + bstr__free (b->data); + + /* In case there is any stale usage, there is one more chance to + notice this error. */ + + b->slen = -1; + b->mlen = -__LINE__; + b->data = NULL; + + bstr__free (b); + return BSTR_OK; +} + +/* int binstr (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * forward. If it is found then return with the first position where it is + * found, otherwise return BSTR_ERR. Note that this is just a brute force + * string searcher that does not attempt clever things like the Boyer-Moore + * search algorithm. Because of this there are many degenerate cases where + * this can take much longer than it needs to. + */ +int binstr (const_bstring b1, int pos, const_bstring b2) { +int j, ii, ll, lf; +unsigned char * d0; +unsigned char c0; +register unsigned char * d1; +register unsigned char c1; +register int i; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + /* No space to find such a string? */ + if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR; + + /* An obvious alias case */ + if (b1->data == b2->data && pos == 0) return 0; + + i = pos; + + d0 = b2->data; + d1 = b1->data; + ll = b2->slen; + + /* Peel off the b2->slen == 1 case */ + c0 = d0[0]; + if (1 == ll) { + for (;i < lf; i++) if (c0 == d1[i]) return i; + return BSTR_ERR; + } + + c1 = c0; + j = 0; + lf = b1->slen - 1; + + ii = -1; + if (i < lf) do { + /* Unrolled current character test */ + if (c1 != d1[i]) { + if (c1 != d1[1+i]) { + i += 2; + continue; + } + i++; + } + + /* Take note if this is the start of a potential match */ + if (0 == j) ii = i; + + /* Shift the test character down by one */ + j++; + i++; + + /* If this isn't past the last character continue */ + if (j < ll) { + c1 = d0[j]; + continue; + } + + N0:; + + /* If no characters mismatched, then we matched */ + if (i == ii+j) return ii; + + /* Shift back to the beginning */ + i -= j; + j = 0; + c1 = c0; + } while (i < lf); + + /* Deal with last case if unrolling caused a misalignment */ + if (i == lf && ll == j+1 && c1 == d1[i]) goto N0; + + return BSTR_ERR; +} + +/* int binstrr (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * backward. If it is found then return with the first position where it is + * found, otherwise return BSTR_ERR. Note that this is just a brute force + * string searcher that does not attempt clever things like the Boyer-Moore + * search algorithm. Because of this there are many degenerate cases where + * this can take much longer than it needs to. + */ +int binstrr (const_bstring b1, int pos, const_bstring b2) { +int j, i, l; +unsigned char * d0, * d1; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos && b2->slen == 0) return pos; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + /* Obvious alias case */ + if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0; + + i = pos; + if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR; + + /* If no space to find such a string then snap back */ + if (l + 1 <= i) i = l; + j = 0; + + d0 = b2->data; + d1 = b1->data; + l = b2->slen; + + for (;;) { + if (d0[j] == d1[i + j]) { + j ++; + if (j >= l) return i; + } else { + i --; + if (i < 0) break; + j=0; + } + } + + return BSTR_ERR; +} + +/* int binstrcaseless (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * forward but without regard to case. If it is found then return with the + * first position where it is found, otherwise return BSTR_ERR. Note that + * this is just a brute force string searcher that does not attempt clever + * things like the Boyer-Moore search algorithm. Because of this there are + * many degenerate cases where this can take much longer than it needs to. + */ +int binstrcaseless (const_bstring b1, int pos, const_bstring b2) { +int j, i, l, ll; +unsigned char * d0, * d1; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + l = b1->slen - b2->slen + 1; + + /* No space to find such a string? */ + if (l <= pos) return BSTR_ERR; + + /* An obvious alias case */ + if (b1->data == b2->data && pos == 0) return BSTR_OK; + + i = pos; + j = 0; + + d0 = b2->data; + d1 = b1->data; + ll = b2->slen; + + for (;;) { + if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) { + j ++; + if (j >= ll) return i; + } else { + i ++; + if (i >= l) break; + j=0; + } + } + + return BSTR_ERR; +} + +/* int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * backward but without regard to case. If it is found then return with the + * first position where it is found, otherwise return BSTR_ERR. Note that + * this is just a brute force string searcher that does not attempt clever + * things like the Boyer-Moore search algorithm. Because of this there are + * many degenerate cases where this can take much longer than it needs to. + */ +int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) { +int j, i, l; +unsigned char * d0, * d1; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos && b2->slen == 0) return pos; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + /* Obvious alias case */ + if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK; + + i = pos; + if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR; + + /* If no space to find such a string then snap back */ + if (l + 1 <= i) i = l; + j = 0; + + d0 = b2->data; + d1 = b1->data; + l = b2->slen; + + for (;;) { + if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) { + j ++; + if (j >= l) return i; + } else { + i --; + if (i < 0) break; + j=0; + } + } + + return BSTR_ERR; +} + + +/* int bstrchrp (const_bstring b, int c, int pos) + * + * Search for the character c in b forwards from the position pos + * (inclusive). + */ +int bstrchrp (const_bstring b, int c, int pos) { +unsigned char * p; + + if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR; + p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos)); + if (p) return (int) (p - b->data); + return BSTR_ERR; +} + +/* int bstrrchrp (const_bstring b, int c, int pos) + * + * Search for the character c in b backwards from the position pos in string + * (inclusive). + */ +int bstrrchrp (const_bstring b, int c, int pos) { +int i; + + if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR; + for (i=pos; i >= 0; i--) { + if (b->data[i] == (unsigned char) c) return i; + } + return BSTR_ERR; +} + +#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF) +#define LONG_LOG_BITS_QTY (3) +#define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY) +#define LONG_TYPE unsigned char + +#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY) +struct charField { LONG_TYPE content[CFCLEN]; }; +#define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1)))) +#define setInCharField(cf,idx) { \ + unsigned int c = (unsigned int) (idx); \ + (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \ +} + +#else + +#define CFCLEN (1 << CHAR_BIT) +struct charField { unsigned char content[CFCLEN]; }; +#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)]) +#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0 + +#endif + +/* Convert a bstring to charField */ +static int buildCharField (struct charField * cf, const_bstring b) { +int i; + if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR; + memset ((void *) cf->content, 0, sizeof (struct charField)); + for (i=0; i < b->slen; i++) { + setInCharField (cf, b->data[i]); + } + return BSTR_OK; +} + +static void invertCharField (struct charField * cf) { +int i; + for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i]; +} + +/* Inner engine for binchr */ +static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) { +int i; + for (i=pos; i < len; i++) { + unsigned char c = (unsigned char) data[i]; + if (testInCharField (cf, c)) return i; + } + return BSTR_ERR; +} + +/* int binchr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the first position in b0 starting from pos or after, in which + * one of the characters in b1 is found and return it. If such a position + * does not exist in b0, then BSTR_ERR is returned. + */ +int binchr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || + b0->slen <= pos) return BSTR_ERR; + if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos); + if (0 > buildCharField (&chrs, b1)) return BSTR_ERR; + return binchrCF (b0->data, b0->slen, pos, &chrs); +} + +/* Inner engine for binchrr */ +static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) { +int i; + for (i=pos; i >= 0; i--) { + unsigned int c = (unsigned int) data[i]; + if (testInCharField (cf, c)) return i; + } + return BSTR_ERR; +} + +/* int binchrr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the last position in b0 no greater than pos, in which one of + * the characters in b1 is found and return it. If such a position does not + * exist in b0, then BSTR_ERR is returned. + */ +int binchrr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL || + b0->slen < pos) return BSTR_ERR; + if (pos == b0->slen) pos--; + if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos); + if (0 > buildCharField (&chrs, b1)) return BSTR_ERR; + return binchrrCF (b0->data, pos, &chrs); +} + +/* int bninchr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the first position in b0 starting from pos or after, in which + * none of the characters in b1 is found and return it. If such a position + * does not exist in b0, then BSTR_ERR is returned. + */ +int bninchr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || + b0->slen <= pos) return BSTR_ERR; + if (buildCharField (&chrs, b1) < 0) return BSTR_ERR; + invertCharField (&chrs); + return binchrCF (b0->data, b0->slen, pos, &chrs); +} + +/* int bninchrr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the last position in b0 no greater than pos, in which none of + * the characters in b1 is found and return it. If such a position does not + * exist in b0, then BSTR_ERR is returned. + */ +int bninchrr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || + b0->slen < pos) return BSTR_ERR; + if (pos == b0->slen) pos--; + if (buildCharField (&chrs, b1) < 0) return BSTR_ERR; + invertCharField (&chrs); + return binchrrCF (b0->data, pos, &chrs); +} + +/* int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill) + * + * Overwrite the string b0 starting at position pos with the string b1. If + * the position pos is past the end of b0, then the character "fill" is + * appended as necessary to make up the gap between the end of b0 and pos. + * If b1 is NULL, it behaves as if it were a 0-length string. + */ +int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) { +int d, newlen; +ptrdiff_t pd; +bstring aux = (bstring) b1; + + if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || + b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR; + if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR; + + d = pos; + + /* Aliasing case */ + if (NULL != aux) { + if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) { + if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; + } + d += aux->slen; + } + + /* Increase memory size if necessary */ + if (balloc (b0, d + 1) != BSTR_OK) { + if (aux != b1) bdestroy (aux); + return BSTR_ERR; + } + + newlen = b0->slen; + + /* Fill in "fill" character as necessary */ + if (pos > newlen) { + bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen)); + newlen = pos; + } + + /* Copy b1 to position pos in b0. */ + if (aux != NULL) { + bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen); + if (aux != b1) bdestroy (aux); + } + + /* Indicate the potentially increased size of b0 */ + if (d > newlen) newlen = d; + + b0->slen = newlen; + b0->data[newlen] = (unsigned char) '\0'; + + return BSTR_OK; +} + +/* int binsert (bstring b1, int pos, bstring b2, unsigned char fill) + * + * Inserts the string b2 into b1 at position pos. If the position pos is + * past the end of b1, then the character "fill" is appended as necessary to + * make up the gap between the end of b1 and pos. Unlike bsetstr, binsert + * does not allow b2 to be NULL. + */ +int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) { +int d, l; +ptrdiff_t pd; +bstring aux = (bstring) b2; + + if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || + b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR; + + /* Aliasing case */ + if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) { + if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR; + } + + /* Compute the two possible end pointers */ + d = b1->slen + aux->slen; + l = pos + aux->slen; + if ((d|l) < 0) return BSTR_ERR; + + if (l > d) { + /* Inserting past the end of the string */ + if (balloc (b1, l + 1) != BSTR_OK) { + if (aux != b2) bdestroy (aux); + return BSTR_ERR; + } + bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen)); + b1->slen = l; + } else { + /* Inserting in the middle of the string */ + if (balloc (b1, d + 1) != BSTR_OK) { + if (aux != b2) bdestroy (aux); + return BSTR_ERR; + } + bBlockCopy (b1->data + l, b1->data + pos, d - l); + b1->slen = d; + } + bBlockCopy (b1->data + pos, aux->data, aux->slen); + b1->data[b1->slen] = (unsigned char) '\0'; + if (aux != b2) bdestroy (aux); + return BSTR_OK; +} + +/* int breplace (bstring b1, int pos, int len, bstring b2, + * unsigned char fill) + * + * Replace a section of a string from pos for a length len with the string b2. + * fill is used is pos > b1->slen. + */ +int breplace (bstring b1, int pos, int len, const_bstring b2, + unsigned char fill) { +int pl, ret; +ptrdiff_t pd; +bstring aux = (bstring) b2; + + if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL || + b2 == NULL || b1->data == NULL || b2->data == NULL || + b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen || + b1->mlen <= 0) return BSTR_ERR; + + /* Straddles the end? */ + if (pl >= b1->slen) { + if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret; + if (pos + b2->slen < b1->slen) { + b1->slen = pos + b2->slen; + b1->data[b1->slen] = (unsigned char) '\0'; + } + return ret; + } + + /* Aliasing case */ + if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) { + if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR; + } + + if (aux->slen > len) { + if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) { + if (aux != b2) bdestroy (aux); + return BSTR_ERR; + } + } + + if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len)); + bstr__memcpy (b1->data + pos, aux->data, aux->slen); + b1->slen += aux->slen - len; + b1->data[b1->slen] = (unsigned char) '\0'; + if (aux != b2) bdestroy (aux); + return BSTR_OK; +} + +/* + * findreplaceengine is used to implement bfindreplace and + * bfindreplacecaseless. It works by breaking the three cases of + * expansion, reduction and replacement, and solving each of these + * in the most efficient way possible. + */ + +typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2); + +#define INITIAL_STATIC_FIND_INDEX_COUNT 32 + +static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) { +int i, ret, slen, mlen, delta, acc; +int * d; +int static_d[INITIAL_STATIC_FIND_INDEX_COUNT+1]; /* This +1 is unnecessary, but it shuts up LINT. */ +ptrdiff_t pd; +bstring auxf = (bstring) find; +bstring auxr = (bstring) repl; + + if (b == NULL || b->data == NULL || find == NULL || + find->data == NULL || repl == NULL || repl->data == NULL || + pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen || + b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR; + if (pos > b->slen - find->slen) return BSTR_OK; + + /* Alias with find string */ + pd = (ptrdiff_t) (find->data - b->data); + if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) { + if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR; + } + + /* Alias with repl string */ + pd = (ptrdiff_t) (repl->data - b->data); + if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) { + if (NULL == (auxr = bstrcpy (repl))) { + if (auxf != find) bdestroy (auxf); + return BSTR_ERR; + } + } + + delta = auxf->slen - auxr->slen; + + /* in-place replacement since find and replace strings are of equal + length */ + if (delta == 0) { + while ((pos = instr (b, pos, auxf)) >= 0) { + bstr__memcpy (b->data + pos, auxr->data, auxr->slen); + pos += auxf->slen; + } + if (auxf != find) bdestroy (auxf); + if (auxr != repl) bdestroy (auxr); + return BSTR_OK; + } + + /* shrinking replacement since auxf->slen > auxr->slen */ + if (delta > 0) { + acc = 0; + + while ((i = instr (b, pos, auxf)) >= 0) { + if (acc && i > pos) + bstr__memmove (b->data + pos - acc, b->data + pos, i - pos); + if (auxr->slen) + bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen); + acc += delta; + pos = i + auxf->slen; + } + + if (acc) { + i = b->slen; + if (i > pos) + bstr__memmove (b->data + pos - acc, b->data + pos, i - pos); + b->slen -= acc; + b->data[b->slen] = (unsigned char) '\0'; + } + + if (auxf != find) bdestroy (auxf); + if (auxr != repl) bdestroy (auxr); + return BSTR_OK; + } + + /* expanding replacement since find->slen < repl->slen. Its a lot + more complicated. This works by first finding all the matches and + storing them to a growable array, then doing at most one resize of + the destination bstring and then performing the direct memory transfers + of the string segment pieces to form the final result. The growable + array of matches uses a deferred doubling reallocing strategy. What + this means is that it starts as a reasonably fixed sized auto array in + the hopes that many if not most cases will never need to grow this + array. But it switches as soon as the bounds of the array will be + exceeded. An extra find result is always appended to this array that + corresponds to the end of the destination string, so slen is checked + against mlen - 1 rather than mlen before resizing. + */ + + mlen = INITIAL_STATIC_FIND_INDEX_COUNT; + d = (int *) static_d; /* Avoid malloc for trivial/initial cases */ + acc = slen = 0; + + while ((pos = instr (b, pos, auxf)) >= 0) { + if (slen >= mlen - 1) { + int sl, *t; + + mlen += mlen; + sl = sizeof (int *) * mlen; + if (static_d == d) d = NULL; /* static_d cannot be realloced */ + if (mlen <= 0 || sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) { + ret = BSTR_ERR; + goto done; + } + if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d)); + d = t; + } + d[slen] = pos; + slen++; + acc -= delta; + pos += auxf->slen; + if (pos < 0 || acc < 0) { + ret = BSTR_ERR; + goto done; + } + } + + /* slen <= INITIAL_STATIC_INDEX_COUNT-1 or mlen-1 here. */ + d[slen] = b->slen; + + if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) { + b->slen += acc; + for (i = slen-1; i >= 0; i--) { + int s, l; + s = d[i] + auxf->slen; + l = d[i+1] - s; /* d[slen] may be accessed here. */ + if (l) { + bstr__memmove (b->data + s + acc, b->data + s, l); + } + if (auxr->slen) { + bstr__memmove (b->data + s + acc - auxr->slen, + auxr->data, auxr->slen); + } + acc += delta; + } + b->data[b->slen] = (unsigned char) '\0'; + } + + done:; + if (static_d == d) d = NULL; + bstr__free (d); + if (auxf != find) bdestroy (auxf); + if (auxr != repl) bdestroy (auxr); + return ret; +} + +/* int bfindreplace (bstring b, const_bstring find, const_bstring repl, + * int pos) + * + * Replace all occurrences of a find string with a replace string after a + * given point in a bstring. + */ +int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) { + return findreplaceengine (b, find, repl, pos, binstr); +} + +/* int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, + * int pos) + * + * Replace all occurrences of a find string, ignoring case, with a replace + * string after a given point in a bstring. + */ +int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) { + return findreplaceengine (b, find, repl, pos, binstrcaseless); +} + +/* int binsertch (bstring b, int pos, int len, unsigned char fill) + * + * Inserts the character fill repeatedly into b at position pos for a + * length len. If the position pos is past the end of b, then the + * character "fill" is appended as necessary to make up the gap between the + * end of b and the position pos + len. + */ +int binsertch (bstring b, int pos, int len, unsigned char fill) { +int d, l, i; + + if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || len < 0) return BSTR_ERR; + + /* Compute the two possible end pointers */ + d = b->slen + len; + l = pos + len; + if ((d|l) < 0) return BSTR_ERR; + + if (l > d) { + /* Inserting past the end of the string */ + if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR; + pos = b->slen; + b->slen = l; + } else { + /* Inserting in the middle of the string */ + if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR; + for (i = d - 1; i >= l; i--) { + b->data[i] = b->data[i - len]; + } + b->slen = d; + } + + for (i=pos; i < l; i++) b->data[i] = fill; + b->data[b->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bpattern (bstring b, int len) + * + * Replicate the bstring, b in place, end to end repeatedly until it + * surpasses len characters, then chop the result to exactly len characters. + * This function operates in-place. The function will return with BSTR_ERR + * if b is NULL or of length 0, otherwise BSTR_OK is returned. + */ +int bpattern (bstring b, int len) { +int i, d; + + d = blength (b); + if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR; + if (len > 0) { + if (d == 1) return bsetstr (b, len, NULL, b->data[0]); + for (i = d; i < len; i++) b->data[i] = b->data[i - d]; + } + b->data[len] = (unsigned char) '\0'; + b->slen = len; + return BSTR_OK; +} + +#define BS_BUFF_SZ (1024) + +/* int breada (bstring b, bNread readPtr, void * parm) + * + * Use a finite buffer fread-like function readPtr to concatenate to the + * bstring b the entire contents of file-like source data in a roughly + * efficient way. + */ +int breada (bstring b, bNread readPtr, void * parm) { +int i, l, n; + + if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || readPtr == NULL) return BSTR_ERR; + + i = b->slen; + for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) { + if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR; + l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm); + i += l; + b->slen = i; + if (i < n) break; + } + + b->data[i] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* bstring bread (bNread readPtr, void * parm) + * + * Use a finite buffer fread-like function readPtr to create a bstring + * filled with the entire contents of file-like source data in a roughly + * efficient way. + */ +bstring bread (bNread readPtr, void * parm) { +bstring buff; + + if (0 > breada (buff = bfromcstr (""), readPtr, parm)) { + bdestroy (buff); + return NULL; + } + return buff; +} + +/* int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) + * + * Use an fgetc-like single character stream reading function (getcPtr) to + * obtain a sequence of characters which are concatenated to the end of the + * bstring b. The stream read is terminated by the passed in terminator + * parameter. + * + * If getcPtr returns with a negative number, or the terminator character + * (which is appended) is read, then the stream reading is halted and the + * function returns with a partial result in b. If there is an empty partial + * result, 1 is returned. If no characters are read, or there is some other + * detectable error, BSTR_ERR is returned. + */ +int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) { +int c, d, e; + + if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR; + d = 0; + e = b->mlen - 2; + + while ((c = getcPtr (parm)) >= 0) { + if (d > e) { + b->slen = d; + if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; + e = b->mlen - 2; + } + b->data[d] = (unsigned char) c; + d++; + if (c == terminator) break; + } + + b->data[d] = (unsigned char) '\0'; + b->slen = d; + + return d == 0 && c < 0; +} + +/* int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) + * + * Use an fgetc-like single character stream reading function (getcPtr) to + * obtain a sequence of characters which are concatenated to the end of the + * bstring b. The stream read is terminated by the passed in terminator + * parameter. + * + * If getcPtr returns with a negative number, or the terminator character + * (which is appended) is read, then the stream reading is halted and the + * function returns with a partial result concatentated to b. If there is + * an empty partial result, 1 is returned. If no characters are read, or + * there is some other detectable error, BSTR_ERR is returned. + */ +int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) { +int c, d, e; + + if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR; + d = b->slen; + e = b->mlen - 2; + + while ((c = getcPtr (parm)) >= 0) { + if (d > e) { + b->slen = d; + if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; + e = b->mlen - 2; + } + b->data[d] = (unsigned char) c; + d++; + if (c == terminator) break; + } + + b->data[d] = (unsigned char) '\0'; + b->slen = d; + + return d == 0 && c < 0; +} + +/* bstring bgets (bNgetc getcPtr, void * parm, char terminator) + * + * Use an fgetc-like single character stream reading function (getcPtr) to + * obtain a sequence of characters which are concatenated into a bstring. + * The stream read is terminated by the passed in terminator function. + * + * If getcPtr returns with a negative number, or the terminator character + * (which is appended) is read, then the stream reading is halted and the + * result obtained thus far is returned. If no characters are read, or + * there is some other detectable error, NULL is returned. + */ +bstring bgets (bNgetc getcPtr, void * parm, char terminator) { +bstring buff; + + if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) { + bdestroy (buff); + buff = NULL; + } + return buff; +} + +struct bStream { + bstring buff; /* Buffer for over-reads */ + void * parm; /* The stream handle for core stream */ + bNread readFnPtr; /* fread compatible fnptr for core stream */ + int isEOF; /* track file's EOF state */ + int maxBuffSz; +}; + +/* struct bStream * bsopen (bNread readPtr, void * parm) + * + * Wrap a given open stream (described by a fread compatible function + * pointer and stream handle) into an open bStream suitable for the bstring + * library streaming functions. + */ +struct bStream * bsopen (bNread readPtr, void * parm) { +struct bStream * s; + + if (readPtr == NULL) return NULL; + s = (struct bStream *) bstr__alloc (sizeof (struct bStream)); + if (s == NULL) return NULL; + s->parm = parm; + s->buff = bfromcstr (""); + s->readFnPtr = readPtr; + s->maxBuffSz = BS_BUFF_SZ; + s->isEOF = 0; + return s; +} + +/* int bsbufflength (struct bStream * s, int sz) + * + * Set the length of the buffer used by the bStream. If sz is zero, the + * length is not set. This function returns with the previous length. + */ +int bsbufflength (struct bStream * s, int sz) { +int oldSz; + if (s == NULL || sz < 0) return BSTR_ERR; + oldSz = s->maxBuffSz; + if (sz > 0) s->maxBuffSz = sz; + return oldSz; +} + +int bseof (const struct bStream * s) { + if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR; + return s->isEOF && (s->buff->slen == 0); +} + +/* void * bsclose (struct bStream * s) + * + * Close the bStream, and return the handle to the stream that was originally + * used to open the given stream. + */ +void * bsclose (struct bStream * s) { +void * parm; + if (s == NULL) return NULL; + s->readFnPtr = NULL; + if (s->buff) bdestroy (s->buff); + s->buff = NULL; + parm = s->parm; + s->parm = NULL; + s->isEOF = 1; + bstr__free (s); + return parm; +} + +/* int bsreadlna (bstring r, struct bStream * s, char terminator) + * + * Read a bstring terminated by the terminator character or the end of the + * stream from the bStream (s) and return it into the parameter r. This + * function may read additional characters from the core stream that are not + * returned, but will be retained for subsequent read operations. + */ +int bsreadlna (bstring r, struct bStream * s, char terminator) { +int i, l, ret, rlo; +char * b; +struct tagbstring x; + + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 || + r->slen < 0 || r->mlen < r->slen) return BSTR_ERR; + l = s->buff->slen; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + b = (char *) s->buff->data; + x.data = (unsigned char *) b; + + /* First check if the current buffer holds the terminator */ + b[l] = terminator; /* Set sentinel */ + for (i=0; b[i] != terminator; i++) ; + if (i < l) { + x.slen = i + 1; + ret = bconcat (r, &x); + s->buff->slen = l; + if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1); + return BSTR_OK; + } + + rlo = r->slen; + + /* If not then just concatenate the entire buffer to the output */ + x.slen = l; + if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR; + + /* Perform direct in-place reads into the destination to allow for + the minimum of data-copies */ + for (;;) { + if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR; + b = (char *) (r->data + r->slen); + l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm); + if (l <= 0) { + r->data[r->slen] = (unsigned char) '\0'; + s->buff->slen = 0; + s->isEOF = 1; + /* If nothing was read return with an error message */ + return BSTR_ERR & -(r->slen == rlo); + } + b[l] = terminator; /* Set sentinel */ + for (i=0; b[i] != terminator; i++) ; + if (i < l) break; + r->slen += l; + } + + /* Terminator found, push over-read back to buffer */ + i++; + r->slen += i; + s->buff->slen = l - i; + bstr__memcpy (s->buff->data, b + i, l - i); + r->data[r->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bsreadlnsa (bstring r, struct bStream * s, bstring term) + * + * Read a bstring terminated by any character in the term string or the end + * of the stream from the bStream (s) and return it into the parameter r. + * This function may read additional characters from the core stream that + * are not returned, but will be retained for subsequent read operations. + */ +int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) { +int i, l, ret, rlo; +unsigned char * b; +struct tagbstring x; +struct charField cf; + + if (s == NULL || s->buff == NULL || r == NULL || term == NULL || + term->data == NULL || r->mlen <= 0 || r->slen < 0 || + r->mlen < r->slen) return BSTR_ERR; + if (term->slen == 1) return bsreadlna (r, s, term->data[0]); + if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR; + + l = s->buff->slen; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + b = (unsigned char *) s->buff->data; + x.data = b; + + /* First check if the current buffer holds the terminator */ + b[l] = term->data[0]; /* Set sentinel */ + for (i=0; !testInCharField (&cf, b[i]); i++) ; + if (i < l) { + x.slen = i + 1; + ret = bconcat (r, &x); + s->buff->slen = l; + if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1); + return BSTR_OK; + } + + rlo = r->slen; + + /* If not then just concatenate the entire buffer to the output */ + x.slen = l; + if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR; + + /* Perform direct in-place reads into the destination to allow for + the minimum of data-copies */ + for (;;) { + if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR; + b = (unsigned char *) (r->data + r->slen); + l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm); + if (l <= 0) { + r->data[r->slen] = (unsigned char) '\0'; + s->buff->slen = 0; + s->isEOF = 1; + /* If nothing was read return with an error message */ + return BSTR_ERR & -(r->slen == rlo); + } + + b[l] = term->data[0]; /* Set sentinel */ + for (i=0; !testInCharField (&cf, b[i]); i++) ; + if (i < l) break; + r->slen += l; + } + + /* Terminator found, push over-read back to buffer */ + i++; + r->slen += i; + s->buff->slen = l - i; + bstr__memcpy (s->buff->data, b + i, l - i); + r->data[r->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bsreada (bstring r, struct bStream * s, int n) + * + * Read a bstring of length n (or, if it is fewer, as many bytes as is + * remaining) from the bStream. This function may read additional + * characters from the core stream that are not returned, but will be + * retained for subsequent read operations. This function will not read + * additional characters from the core stream beyond virtual stream pointer. + */ +int bsreada (bstring r, struct bStream * s, int n) { +int l, ret, orslen; +char * b; +struct tagbstring x; + + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 + || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR; + + n += r->slen; + if (n <= 0) return BSTR_ERR; + + l = s->buff->slen; + + orslen = r->slen; + + if (0 == l) { + if (s->isEOF) return BSTR_ERR; + if (r->mlen > n) { + l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm); + if (0 >= l || l > n - r->slen) { + s->isEOF = 1; + return BSTR_ERR; + } + r->slen += l; + r->data[r->slen] = (unsigned char) '\0'; + return 0; + } + } + + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + b = (char *) s->buff->data; + x.data = (unsigned char *) b; + + do { + if (l + r->slen >= n) { + x.slen = n - r->slen; + ret = bconcat (r, &x); + s->buff->slen = l; + if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen); + return BSTR_ERR & -(r->slen == orslen); + } + + x.slen = l; + if (BSTR_OK != bconcat (r, &x)) break; + + l = n - r->slen; + if (l > s->maxBuffSz) l = s->maxBuffSz; + + l = (int) s->readFnPtr (b, 1, l, s->parm); + + } while (l > 0); + if (l < 0) l = 0; + if (l == 0) s->isEOF = 1; + s->buff->slen = l; + return BSTR_ERR & -(r->slen == orslen); +} + +/* int bsreadln (bstring r, struct bStream * s, char terminator) + * + * Read a bstring terminated by the terminator character or the end of the + * stream from the bStream (s) and return it into the parameter r. This + * function may read additional characters from the core stream that are not + * returned, but will be retained for subsequent read operations. + */ +int bsreadln (bstring r, struct bStream * s, char terminator) { + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0) + return BSTR_ERR; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + r->slen = 0; + return bsreadlna (r, s, terminator); +} + +/* int bsreadlns (bstring r, struct bStream * s, bstring term) + * + * Read a bstring terminated by any character in the term string or the end + * of the stream from the bStream (s) and return it into the parameter r. + * This function may read additional characters from the core stream that + * are not returned, but will be retained for subsequent read operations. + */ +int bsreadlns (bstring r, struct bStream * s, const_bstring term) { + if (s == NULL || s->buff == NULL || r == NULL || term == NULL + || term->data == NULL || r->mlen <= 0) return BSTR_ERR; + if (term->slen == 1) return bsreadln (r, s, term->data[0]); + if (term->slen < 1) return BSTR_ERR; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + r->slen = 0; + return bsreadlnsa (r, s, term); +} + +/* int bsread (bstring r, struct bStream * s, int n) + * + * Read a bstring of length n (or, if it is fewer, as many bytes as is + * remaining) from the bStream. This function may read additional + * characters from the core stream that are not returned, but will be + * retained for subsequent read operations. This function will not read + * additional characters from the core stream beyond virtual stream pointer. + */ +int bsread (bstring r, struct bStream * s, int n) { + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 + || n <= 0) return BSTR_ERR; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + r->slen = 0; + return bsreada (r, s, n); +} + +/* int bsunread (struct bStream * s, const_bstring b) + * + * Insert a bstring into the bStream at the current position. These + * characters will be read prior to those that actually come from the core + * stream. + */ +int bsunread (struct bStream * s, const_bstring b) { + if (s == NULL || s->buff == NULL) return BSTR_ERR; + return binsert (s->buff, 0, b, (unsigned char) '?'); +} + +/* int bspeek (bstring r, const struct bStream * s) + * + * Return the currently buffered characters from the bStream that will be + * read prior to reads from the core stream. + */ +int bspeek (bstring r, const struct bStream * s) { + if (s == NULL || s->buff == NULL) return BSTR_ERR; + return bassign (r, s->buff); +} + +/* bstring bjoin (const struct bstrList * bl, const_bstring sep); + * + * Join the entries of a bstrList into one bstring by sequentially + * concatenating them with the sep string in between. If there is an error + * NULL is returned, otherwise a bstring with the correct result is returned. + */ +bstring bjoin (const struct bstrList * bl, const_bstring sep) { +bstring b; +int i, c, v; + + if (bl == NULL || bl->qty < 0) return NULL; + if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL; + + for (i = 0, c = 1; i < bl->qty; i++) { + v = bl->entry[i]->slen; + if (v < 0) return NULL; /* Invalid input */ + c += v; + if (c < 0) return NULL; /* Wrap around ?? */ + } + + if (sep != NULL) c += (bl->qty - 1) * sep->slen; + + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (NULL == b) return NULL; /* Out of memory */ + b->data = (unsigned char *) bstr__alloc (c); + if (b->data == NULL) { + bstr__free (b); + return NULL; + } + + b->mlen = c; + b->slen = c-1; + + for (i = 0, c = 0; i < bl->qty; i++) { + if (i > 0 && sep != NULL) { + bstr__memcpy (b->data + c, sep->data, sep->slen); + c += sep->slen; + } + v = bl->entry[i]->slen; + bstr__memcpy (b->data + c, bl->entry[i]->data, v); + c += v; + } + b->data[c] = (unsigned char) '\0'; + return b; +} + +#define BSSSC_BUFF_LEN (256) + +/* int bssplitscb (struct bStream * s, const_bstring splitStr, + * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) + * + * Iterate the set of disjoint sequential substrings read from a stream + * divided by any of the characters in splitStr. An empty splitStr causes + * the whole stream to be iterated once. + * + * Note: At the point of calling the cb function, the bStream pointer is + * pointed exactly at the position right after having read the split + * character. The cb function can act on the stream by causing the bStream + * pointer to move, and bssplitscb will continue by starting the next split + * at the position of the pointer after the return from cb. + * + * However, if the cb causes the bStream s to be destroyed then the cb must + * return with a negative value, otherwise bssplitscb will continue in an + * undefined manner. + */ +int bssplitscb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) { +struct charField chrs; +bstring buff; +int i, p, ret; + + if (cb == NULL || s == NULL || s->readFnPtr == NULL + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + + if (NULL == (buff = bfromcstr (""))) return BSTR_ERR; + + if (splitStr->slen == 0) { + while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ; + if ((ret = cb (parm, 0, buff)) > 0) + ret = 0; + } else { + buildCharField (&chrs, splitStr); + ret = p = i = 0; + for (;;) { + if (i >= buff->slen) { + bsreada (buff, s, BSSSC_BUFF_LEN); + if (i >= buff->slen) { + if (0 < (ret = cb (parm, p, buff))) ret = 0; + break; + } + } + if (testInCharField (&chrs, buff->data[i])) { + struct tagbstring t; + unsigned char c; + + blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1)); + if ((ret = bsunread (s, &t)) < 0) break; + buff->slen = i; + c = buff->data[i]; + buff->data[i] = (unsigned char) '\0'; + if ((ret = cb (parm, p, buff)) < 0) break; + buff->data[i] = c; + buff->slen = 0; + p += i + 1; + i = -1; + } + i++; + } + } + + bdestroy (buff); + return ret; +} + +/* int bssplitstrcb (struct bStream * s, const_bstring splitStr, + * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) + * + * Iterate the set of disjoint sequential substrings read from a stream + * divided by the entire substring splitStr. An empty splitStr causes + * each character of the stream to be iterated. + * + * Note: At the point of calling the cb function, the bStream pointer is + * pointed exactly at the position right after having read the split + * character. The cb function can act on the stream by causing the bStream + * pointer to move, and bssplitscb will continue by starting the next split + * at the position of the pointer after the return from cb. + * + * However, if the cb causes the bStream s to be destroyed then the cb must + * return with a negative value, otherwise bssplitscb will continue in an + * undefined manner. + */ +int bssplitstrcb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) { +bstring buff; +int i, p, ret; + + if (cb == NULL || s == NULL || s->readFnPtr == NULL + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + + if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm); + + if (NULL == (buff = bfromcstr (""))) return BSTR_ERR; + + if (splitStr->slen == 0) { + for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) { + if ((ret = cb (parm, 0, buff)) < 0) { + bdestroy (buff); + return ret; + } + buff->slen = 0; + } + return BSTR_OK; + } else { + ret = p = i = 0; + for (i=p=0;;) { + if ((ret = binstr (buff, 0, splitStr)) >= 0) { + struct tagbstring t; + blk2tbstr (t, buff->data, ret); + i = ret + splitStr->slen; + if ((ret = cb (parm, p, &t)) < 0) break; + p += i; + bdelete (buff, 0, i); + } else { + bsreada (buff, s, BSSSC_BUFF_LEN); + if (bseof (s)) { + if ((ret = cb (parm, p, buff)) > 0) ret = 0; + break; + } + } + } + } + + bdestroy (buff); + return ret; +} + +/* int bstrListCreate (void) + * + * Create a bstrList. + */ +struct bstrList * bstrListCreate (void) { +struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (sl) { + sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring)); + if (!sl->entry) { + bstr__free (sl); + sl = NULL; + } else { + sl->qty = 0; + sl->mlen = 1; + } + } + return sl; +} + +/* int bstrListDestroy (struct bstrList * sl) + * + * Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate. + */ +int bstrListDestroy (struct bstrList * sl) { +int i; + if (sl == NULL || sl->qty < 0) return BSTR_ERR; + for (i=0; i < sl->qty; i++) { + if (sl->entry[i]) { + bdestroy (sl->entry[i]); + sl->entry[i] = NULL; + } + } + sl->qty = -1; + sl->mlen = -1; + bstr__free (sl->entry); + sl->entry = NULL; + bstr__free (sl); + return BSTR_OK; +} + +/* int bstrListAlloc (struct bstrList * sl, int msz) + * + * Ensure that there is memory for at least msz number of entries for the + * list. + */ +int bstrListAlloc (struct bstrList * sl, int msz) { +bstring * l; +int smsz; +size_t nsz; + if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR; + if (sl->mlen >= msz) return BSTR_OK; + smsz = snapUpSize (msz); + nsz = ((size_t) smsz) * sizeof (bstring); + if (nsz < (size_t) smsz) return BSTR_ERR; + l = (bstring *) bstr__realloc (sl->entry, nsz); + if (!l) { + smsz = msz; + nsz = ((size_t) smsz) * sizeof (bstring); + l = (bstring *) bstr__realloc (sl->entry, nsz); + if (!l) return BSTR_ERR; + } + sl->mlen = smsz; + sl->entry = l; + return BSTR_OK; +} + +/* int bstrListAllocMin (struct bstrList * sl, int msz) + * + * Try to allocate the minimum amount of memory for the list to include at + * least msz entries or sl->qty whichever is greater. + */ +int bstrListAllocMin (struct bstrList * sl, int msz) { +bstring * l; +size_t nsz; + if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR; + if (msz < sl->qty) msz = sl->qty; + if (sl->mlen == msz) return BSTR_OK; + nsz = ((size_t) msz) * sizeof (bstring); + if (nsz < (size_t) msz) return BSTR_ERR; + l = (bstring *) bstr__realloc (sl->entry, nsz); + if (!l) return BSTR_ERR; + sl->mlen = msz; + sl->entry = l; + return BSTR_OK; +} + +/* int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + * int (* cb) (void * parm, int ofs, int len), void * parm) + * + * Iterate the set of disjoint sequential substrings over str divided by the + * character in splitChar. + * + * Note: Non-destructive modification of str from within the cb function + * while performing this split is not undefined. bsplitcb behaves in + * sequential lock step with calls to cb. I.e., after returning from a cb + * that return a non-negative integer, bsplitcb continues from the position + * 1 character after the last detected split character and it will halt + * immediately if the length of str falls below this point. However, if the + * cb function destroys str, then it *must* return with a negative value, + * otherwise bsplitcb will continue in an undefined manner. + */ +int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm) { +int i, p, ret; + + if (cb == NULL || str == NULL || pos < 0 || pos > str->slen) + return BSTR_ERR; + + p = pos; + do { + for (i=p; i < str->slen; i++) { + if (str->data[i] == splitChar) break; + } + if ((ret = cb (parm, p, i - p)) < 0) return ret; + p = i + 1; + } while (p <= str->slen); + return BSTR_OK; +} + +/* int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + * int (* cb) (void * parm, int ofs, int len), void * parm) + * + * Iterate the set of disjoint sequential substrings over str divided by any + * of the characters in splitStr. An empty splitStr causes the whole str to + * be iterated once. + * + * Note: Non-destructive modification of str from within the cb function + * while performing this split is not undefined. bsplitscb behaves in + * sequential lock step with calls to cb. I.e., after returning from a cb + * that return a non-negative integer, bsplitscb continues from the position + * 1 character after the last detected split character and it will halt + * immediately if the length of str falls below this point. However, if the + * cb function destroys str, then it *must* return with a negative value, + * otherwise bsplitscb will continue in an undefined manner. + */ +int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm) { +struct charField chrs; +int i, p, ret; + + if (cb == NULL || str == NULL || pos < 0 || pos > str->slen + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + if (splitStr->slen == 0) { + if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0; + return ret; + } + + if (splitStr->slen == 1) + return bsplitcb (str, splitStr->data[0], pos, cb, parm); + + buildCharField (&chrs, splitStr); + + p = pos; + do { + for (i=p; i < str->slen; i++) { + if (testInCharField (&chrs, str->data[i])) break; + } + if ((ret = cb (parm, p, i - p)) < 0) return ret; + p = i + 1; + } while (p <= str->slen); + return BSTR_OK; +} + +/* int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + * int (* cb) (void * parm, int ofs, int len), void * parm) + * + * Iterate the set of disjoint sequential substrings over str divided by the + * substring splitStr. An empty splitStr causes the whole str to be + * iterated once. + * + * Note: Non-destructive modification of str from within the cb function + * while performing this split is not undefined. bsplitstrcb behaves in + * sequential lock step with calls to cb. I.e., after returning from a cb + * that return a non-negative integer, bsplitscb continues from the position + * 1 character after the last detected split character and it will halt + * immediately if the length of str falls below this point. However, if the + * cb function destroys str, then it *must* return with a negative value, + * otherwise bsplitscb will continue in an undefined manner. + */ +int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm) { +int i, p, ret; + + if (cb == NULL || str == NULL || pos < 0 || pos > str->slen + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + + if (0 == splitStr->slen) { + for (i=pos; i < str->slen; i++) { + if ((ret = cb (parm, i, 1)) < 0) return ret; + } + return BSTR_OK; + } + + if (splitStr->slen == 1) + return bsplitcb (str, splitStr->data[0], pos, cb, parm); + + for (i=p=pos; i <= str->slen - splitStr->slen; i++) { + if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) { + if ((ret = cb (parm, p, i - p)) < 0) return ret; + i += splitStr->slen; + p = i; + } + } + if ((ret = cb (parm, p, str->slen - p)) < 0) return ret; + return BSTR_OK; +} + +struct genBstrList { + bstring b; + struct bstrList * bl; +}; + +static int bscb (void * parm, int ofs, int len) { +struct genBstrList * g = (struct genBstrList *) parm; + if (g->bl->qty >= g->bl->mlen) { + int mlen = g->bl->mlen * 2; + bstring * tbl; + + while (g->bl->qty >= mlen) { + if (mlen < g->bl->mlen) return BSTR_ERR; + mlen += mlen; + } + + tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen); + if (tbl == NULL) return BSTR_ERR; + + g->bl->entry = tbl; + g->bl->mlen = mlen; + } + + g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len); + g->bl->qty++; + return BSTR_OK; +} + +/* struct bstrList * bsplit (const_bstring str, unsigned char splitChar) + * + * Create an array of sequential substrings from str divided by the character + * splitChar. + */ +struct bstrList * bsplit (const_bstring str, unsigned char splitChar) { +struct genBstrList g; + + if (str == NULL || str->data == NULL || str->slen < 0) return NULL; + + g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (g.bl == NULL) return NULL; + g.bl->mlen = 4; + g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); + if (NULL == g.bl->entry) { + bstr__free (g.bl); + return NULL; + } + + g.b = (bstring) str; + g.bl->qty = 0; + if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) { + bstrListDestroy (g.bl); + return NULL; + } + return g.bl; +} + +/* struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) + * + * Create an array of sequential substrings from str divided by the entire + * substring splitStr. + */ +struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) { +struct genBstrList g; + + if (str == NULL || str->data == NULL || str->slen < 0) return NULL; + + g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (g.bl == NULL) return NULL; + g.bl->mlen = 4; + g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); + if (NULL == g.bl->entry) { + bstr__free (g.bl); + return NULL; + } + + g.b = (bstring) str; + g.bl->qty = 0; + if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) { + bstrListDestroy (g.bl); + return NULL; + } + return g.bl; +} + +/* struct bstrList * bsplits (const_bstring str, bstring splitStr) + * + * Create an array of sequential substrings from str divided by any of the + * characters in splitStr. An empty splitStr causes a single entry bstrList + * containing a copy of str to be returned. + */ +struct bstrList * bsplits (const_bstring str, const_bstring splitStr) { +struct genBstrList g; + + if ( str == NULL || str->slen < 0 || str->data == NULL || + splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL) + return NULL; + + g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (g.bl == NULL) return NULL; + g.bl->mlen = 4; + g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); + if (NULL == g.bl->entry) { + bstr__free (g.bl); + return NULL; + } + g.b = (bstring) str; + g.bl->qty = 0; + + if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) { + bstrListDestroy (g.bl); + return NULL; + } + return g.bl; +} + +#if defined (__TURBOC__) && !defined (__BORLANDC__) +# ifndef BSTRLIB_NOVSNP +# define BSTRLIB_NOVSNP +# endif +#endif + +/* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */ +#if defined(__WATCOMC__) || defined(_MSC_VER) +#define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);} +#else +#ifdef BSTRLIB_NOVSNP +/* This is just a hack. If you are using a system without a vsnprintf, it is + not recommended that bformat be used at all. */ +#define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;} +#define START_VSNBUFF (256) +#else + +#if defined(__GNUC__) && !defined(__clang__) +/* Something is making gcc complain about this prototype not being here, so + I've just gone ahead and put it in. */ +extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg); +#endif + +#define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);} +#endif +#endif + +#if !defined (BSTRLIB_NOVSNP) + +#ifndef START_VSNBUFF +#define START_VSNBUFF (16) +#endif + +/* On IRIX vsnprintf returns n-1 when the operation would overflow the target + buffer, WATCOM and MSVC both return -1, while C99 requires that the + returned value be exactly what the length would be if the buffer would be + large enough. This leads to the idea that if the return value is larger + than n, then changing n to the return value will reduce the number of + iterations required. */ + +/* int bformata (bstring b, const char * fmt, ...) + * + * After the first parameter, it takes the same parameters as printf (), but + * rather than outputting results to stdio, it appends the results to + * a bstring which contains what would have been output. Note that if there + * is an early generation of a '\0' character, the bstring will be truncated + * to this end point. + */ +int bformata (bstring b, const char * fmt, ...) { +va_list arglist; +bstring buff; +int n, r; + + if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 + || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "vsnprintf" call (to avoid buffer + overflows) on increasing potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) { + n = 1; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR; + } + + for (;;) { + va_start (arglist, fmt); + exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); + va_end (arglist); + + buff->data[n] = (unsigned char) '\0'; + buff->slen = (int) (strlen) ((char *) buff->data); + + if (buff->slen < n) break; + + if (r > n) n = r; else n += n; + + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return BSTR_ERR; + } + } + + r = bconcat (b, buff); + bdestroy (buff); + return r; +} + +/* int bassignformat (bstring b, const char * fmt, ...) + * + * After the first parameter, it takes the same parameters as printf (), but + * rather than outputting results to stdio, it outputs the results to + * the bstring parameter b. Note that if there is an early generation of a + * '\0' character, the bstring will be truncated to this end point. + */ +int bassignformat (bstring b, const char * fmt, ...) { +va_list arglist; +bstring buff; +int n, r; + + if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 + || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "vsnprintf" call (to avoid buffer + overflows) on increasing potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) { + n = 1; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR; + } + + for (;;) { + va_start (arglist, fmt); + exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); + va_end (arglist); + + buff->data[n] = (unsigned char) '\0'; + buff->slen = (int) (strlen) ((char *) buff->data); + + if (buff->slen < n) break; + + if (r > n) n = r; else n += n; + + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return BSTR_ERR; + } + } + + r = bassign (b, buff); + bdestroy (buff); + return r; +} + +/* bstring bformat (const char * fmt, ...) + * + * Takes the same parameters as printf (), but rather than outputting results + * to stdio, it forms a bstring which contains what would have been output. + * Note that if there is an early generation of a '\0' character, the + * bstring will be truncated to this end point. + */ +bstring bformat (const char * fmt, ...) { +va_list arglist; +bstring buff; +int n, r; + + if (fmt == NULL) return NULL; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "vsnprintf" call (to avoid buffer + overflows) on increasing potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) { + n = 1; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL; + } + + for (;;) { + va_start (arglist, fmt); + exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); + va_end (arglist); + + buff->data[n] = (unsigned char) '\0'; + buff->slen = (int) (strlen) ((char *) buff->data); + + if (buff->slen < n) break; + + if (r > n) n = r; else n += n; + + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return NULL; + } + } + + return buff; +} + +/* int bvcformata (bstring b, int count, const char * fmt, va_list arglist) + * + * The bvcformata function formats data under control of the format control + * string fmt and attempts to append the result to b. The fmt parameter is + * the same as that of the printf function. The variable argument list is + * replaced with arglist, which has been initialized by the va_start macro. + * The size of the appended output is upper bounded by count. If the + * required output exceeds count, the string b is not augmented with any + * contents and a value below BSTR_ERR is returned. If a value below -count + * is returned then it is recommended that the negative of this value be + * used as an update to the count in a subsequent pass. On other errors, + * such as running out of memory, parameter errors or numeric wrap around + * BSTR_ERR is returned. BSTR_OK is returned when the output is successfully + * generated and appended to b. + * + * Note: There is no sanity checking of arglist, and this function is + * destructive of the contents of b from the b->slen point onward. If there + * is an early generation of a '\0' character, the bstring will be truncated + * to this end point. + */ +int bvcformata (bstring b, int count, const char * fmt, va_list arg) { +int n, r, l; + + if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL + || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; + + if (count > (n = b->slen + count) + 2) return BSTR_ERR; + if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR; + + exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg); + + /* Did the operation complete successfully within bounds? */ + for (l = b->slen; l <= n; l++) { + if ('\0' == b->data[l]) { + b->slen = l; + return BSTR_OK; + } + } + + /* Abort, since the buffer was not large enough. The return value + tries to help set what the retry length should be. */ + + b->data[b->slen] = '\0'; + if (r > count + 1) { /* Does r specify a particular target length? */ + n = r; + } else { + n = count + count; /* If not, just double the size of count */ + if (count > n) n = INT_MAX; + } + n = -n; + + if (n > BSTR_ERR-1) n = BSTR_ERR-1; + return n; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.h b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.h new file mode 100644 index 0000000000..edf8c00fc6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.h @@ -0,0 +1,305 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstrlib.h + * + * This file is the header file for the core module for implementing the + * bstring functions. + */ + +#ifndef BSTRLIB_INCLUDE +#define BSTRLIB_INCLUDE + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP) +# if defined (__TURBOC__) && !defined (__BORLANDC__) +# define BSTRLIB_NOVSNP +# endif +#endif + +#define BSTR_ERR (-1) +#define BSTR_OK (0) +#define BSTR_BS_BUFF_LENGTH_GET (0) + +typedef struct tagbstring * bstring; +typedef const struct tagbstring * const_bstring; + +/* Copy functions */ +#define cstr2bstr bfromcstr +extern bstring bfromcstr (const char * str); +extern bstring bfromcstralloc (int mlen, const char * str); +extern bstring blk2bstr (const void * blk, int len); +extern char * bstr2cstr (const_bstring s, char z); +extern int bcstrfree (char * s); +extern bstring bstrcpy (const_bstring b1); +extern int bassign (bstring a, const_bstring b); +extern int bassignmidstr (bstring a, const_bstring b, int left, int len); +extern int bassigncstr (bstring a, const char * str); +extern int bassignblk (bstring a, const void * s, int len); + +/* Destroy function */ +extern int bdestroy (bstring b); + +/* Space allocation hinting functions */ +extern int balloc (bstring s, int len); +extern int ballocmin (bstring b, int len); + +/* Substring extraction */ +extern bstring bmidstr (const_bstring b, int left, int len); + +/* Various standard manipulations */ +extern int bconcat (bstring b0, const_bstring b1); +extern int bconchar (bstring b0, char c); +extern int bcatcstr (bstring b, const char * s); +extern int bcatblk (bstring b, const void * s, int len); +extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill); +extern int binsertch (bstring s1, int pos, int len, unsigned char fill); +extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill); +extern int bdelete (bstring s1, int pos, int len); +extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill); +extern int btrunc (bstring b, int n); + +/* Scan/search functions */ +extern int bstricmp (const_bstring b0, const_bstring b1); +extern int bstrnicmp (const_bstring b0, const_bstring b1, int n); +extern int biseqcaseless (const_bstring b0, const_bstring b1); +extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len); +extern int biseq (const_bstring b0, const_bstring b1); +extern int bisstemeqblk (const_bstring b0, const void * blk, int len); +extern int biseqcstr (const_bstring b, const char * s); +extern int biseqcstrcaseless (const_bstring b, const char * s); +extern int bstrcmp (const_bstring b0, const_bstring b1); +extern int bstrncmp (const_bstring b0, const_bstring b1, int n); +extern int binstr (const_bstring s1, int pos, const_bstring s2); +extern int binstrr (const_bstring s1, int pos, const_bstring s2); +extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2); +extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2); +extern int bstrchrp (const_bstring b, int c, int pos); +extern int bstrrchrp (const_bstring b, int c, int pos); +#define bstrchr(b,c) bstrchrp ((b), (c), 0) +#define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1) +extern int binchr (const_bstring b0, int pos, const_bstring b1); +extern int binchrr (const_bstring b0, int pos, const_bstring b1); +extern int bninchr (const_bstring b0, int pos, const_bstring b1); +extern int bninchrr (const_bstring b0, int pos, const_bstring b1); +extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos); +extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos); + +/* List of string container functions */ +struct bstrList { + int qty, mlen; + bstring * entry; +}; +extern struct bstrList * bstrListCreate (void); +extern int bstrListDestroy (struct bstrList * sl); +extern int bstrListAlloc (struct bstrList * sl, int msz); +extern int bstrListAllocMin (struct bstrList * sl, int msz); + +/* String split and join functions */ +extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar); +extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr); +extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr); +extern bstring bjoin (const struct bstrList * bl, const_bstring sep); +extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); +extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); +extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + +/* Miscellaneous functions */ +extern int bpattern (bstring b, int len); +extern int btoupper (bstring b); +extern int btolower (bstring b); +extern int bltrimws (bstring b); +extern int brtrimws (bstring b); +extern int btrimws (bstring b); + +/* <*>printf format functions */ +#if !defined (BSTRLIB_NOVSNP) +extern bstring bformat (const char * fmt, ...); +extern int bformata (bstring b, const char * fmt, ...); +extern int bassignformat (bstring b, const char * fmt, ...); +extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist); + +#define bvformata(ret, b, fmt, lastarg) { \ +bstring bstrtmp_b = (b); \ +const char * bstrtmp_fmt = (fmt); \ +int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \ + for (;;) { \ + va_list bstrtmp_arglist; \ + va_start (bstrtmp_arglist, lastarg); \ + bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \ + va_end (bstrtmp_arglist); \ + if (bstrtmp_r >= 0) { /* Everything went ok */ \ + bstrtmp_r = BSTR_OK; \ + break; \ + } else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \ + bstrtmp_r = BSTR_ERR; \ + break; \ + } \ + bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \ + } \ + ret = bstrtmp_r; \ +} + +#endif + +typedef int (*bNgetc) (void *parm); +typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm); + +/* Input functions */ +extern bstring bgets (bNgetc getcPtr, void * parm, char terminator); +extern bstring bread (bNread readPtr, void * parm); +extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator); +extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator); +extern int breada (bstring b, bNread readPtr, void * parm); + +/* Stream functions */ +extern struct bStream * bsopen (bNread readPtr, void * parm); +extern void * bsclose (struct bStream * s); +extern int bsbufflength (struct bStream * s, int sz); +extern int bsreadln (bstring b, struct bStream * s, char terminator); +extern int bsreadlns (bstring r, struct bStream * s, const_bstring term); +extern int bsread (bstring b, struct bStream * s, int n); +extern int bsreadlna (bstring b, struct bStream * s, char terminator); +extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term); +extern int bsreada (bstring b, struct bStream * s, int n); +extern int bsunread (struct bStream * s, const_bstring b); +extern int bspeek (bstring r, const struct bStream * s); +extern int bssplitscb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); +extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); +extern int bseof (const struct bStream * s); + +struct tagbstring { + int mlen; + int slen; + unsigned char * data; +}; + +/* Accessor macros */ +#define blengthe(b, e) (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen)) +#define blength(b) (blengthe ((b), 0)) +#define bdataofse(b, o, e) (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o)) +#define bdataofs(b, o) (bdataofse ((b), (o), (void *)0)) +#define bdatae(b, e) (bdataofse (b, 0, e)) +#define bdata(b) (bdataofs (b, 0)) +#define bchare(b, p, e) ((((unsigned)(p)) < (unsigned)blength(b)) ? ((b)->data[(p)]) : (e)) +#define bchar(b, p) bchare ((b), (p), '\0') + +/* Static constant string initialization macro */ +#define bsStaticMlen(q,m) {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")} +#if defined(_MSC_VER) +/* There are many versions of MSVC which emit __LINE__ as a non-constant. */ +# define bsStatic(q) bsStaticMlen(q,-32) +#endif +#ifndef bsStatic +# define bsStatic(q) bsStaticMlen(q,-__LINE__) +#endif + +/* Static constant block parameter pair */ +#define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1) + +/* Reference building macros */ +#define cstr2tbstr btfromcstr +#define btfromcstr(t,s) { \ + (t).data = (unsigned char *) (s); \ + (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \ + (t).mlen = -1; \ +} +#define blk2tbstr(t,s,l) { \ + (t).data = (unsigned char *) (s); \ + (t).slen = l; \ + (t).mlen = -1; \ +} +#define btfromblk(t,s,l) blk2tbstr(t,s,l) +#define bmid2tbstr(t,b,p,l) { \ + const_bstring bstrtmp_s = (b); \ + if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) { \ + int bstrtmp_left = (p); \ + int bstrtmp_len = (l); \ + if (bstrtmp_left < 0) { \ + bstrtmp_len += bstrtmp_left; \ + bstrtmp_left = 0; \ + } \ + if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left) \ + bstrtmp_len = bstrtmp_s->slen - bstrtmp_left; \ + if (bstrtmp_len <= 0) { \ + (t).data = (unsigned char *)""; \ + (t).slen = 0; \ + } else { \ + (t).data = bstrtmp_s->data + bstrtmp_left; \ + (t).slen = bstrtmp_len; \ + } \ + } else { \ + (t).data = (unsigned char *)""; \ + (t).slen = 0; \ + } \ + (t).mlen = -__LINE__; \ +} +#define btfromblkltrimws(t,s,l) { \ + int bstrtmp_idx = 0, bstrtmp_len = (l); \ + unsigned char * bstrtmp_s = (s); \ + if (bstrtmp_s && bstrtmp_len >= 0) { \ + for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) { \ + if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \ + } \ + } \ + (t).data = bstrtmp_s + bstrtmp_idx; \ + (t).slen = bstrtmp_len - bstrtmp_idx; \ + (t).mlen = -__LINE__; \ +} +#define btfromblkrtrimws(t,s,l) { \ + int bstrtmp_len = (l) - 1; \ + unsigned char * bstrtmp_s = (s); \ + if (bstrtmp_s && bstrtmp_len >= 0) { \ + for (; bstrtmp_len >= 0; bstrtmp_len--) { \ + if (!isspace (bstrtmp_s[bstrtmp_len])) break; \ + } \ + } \ + (t).data = bstrtmp_s; \ + (t).slen = bstrtmp_len + 1; \ + (t).mlen = -__LINE__; \ +} +#define btfromblktrimws(t,s,l) { \ + int bstrtmp_idx = 0, bstrtmp_len = (l) - 1; \ + unsigned char * bstrtmp_s = (s); \ + if (bstrtmp_s && bstrtmp_len >= 0) { \ + for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) { \ + if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \ + } \ + for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) { \ + if (!isspace (bstrtmp_s[bstrtmp_len])) break; \ + } \ + } \ + (t).data = bstrtmp_s + bstrtmp_idx; \ + (t).slen = bstrtmp_len + 1 - bstrtmp_idx; \ + (t).mlen = -__LINE__; \ +} + +/* Write protection macros */ +#define bwriteprotect(t) { if ((t).mlen >= 0) (t).mlen = -1; } +#define bwriteallow(t) { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); } +#define biswriteprotected(t) ((t).mlen <= 0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.txt b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.txt new file mode 100644 index 0000000000..8ebb188853 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/bstrlib.txt @@ -0,0 +1,3201 @@ +Better String library +--------------------- + +by Paul Hsieh + +The bstring library is an attempt to provide improved string processing +functionality to the C and C++ language. At the heart of the bstring library +(Bstrlib for short) is the management of "bstring"s which are a significant +improvement over '\0' terminated char buffers. + +=============================================================================== + +Motivation +---------- + +The standard C string library has serious problems: + + 1) Its use of '\0' to denote the end of the string means knowing a + string's length is O(n) when it could be O(1). + 2) It imposes an interpretation for the character value '\0'. + 3) gets() always exposes the application to a buffer overflow. + 4) strtok() modifies the string its parsing and thus may not be usable in + programs which are re-entrant or multithreaded. + 5) fgets has the unusual semantic of ignoring '\0's that occur before + '\n's are consumed. + 6) There is no memory management, and actions performed such as strcpy, + strcat and sprintf are common places for buffer overflows. + 7) strncpy() doesn't '\0' terminate the destination in some cases. + 8) Passing NULL to C library string functions causes an undefined NULL + pointer access. + 9) Parameter aliasing (overlapping, or self-referencing parameters) + within most C library functions has undefined behavior. + 10) Many C library string function calls take integer parameters with + restricted legal ranges. Parameters passed outside these ranges are + not typically detected and cause undefined behavior. + +So the desire is to create an alternative string library that does not suffer +from the above problems and adds in the following functionality: + + 1) Incorporate string functionality seen from other languages. + a) MID$() - from BASIC + b) split()/join() - from Python + c) string/char x n - from Perl + 2) Implement analogs to functions that combine stream IO and char buffers + without creating a dependency on stream IO functionality. + 3) Implement the basic text editor-style functions insert, delete, find, + and replace. + 4) Implement reference based sub-string access (as a generalization of + pointer arithmetic.) + 5) Implement runtime write protection for strings. + +There is also a desire to avoid "API-bloat". So functionality that can be +implemented trivially in other functionality is omitted. So there is no +left$() or right$() or reverse() or anything like that as part of the core +functionality. + +Explaining Bstrings +------------------- + +A bstring is basically a header which wraps a pointer to a char buffer. Lets +start with the declaration of a struct tagbstring: + + struct tagbstring { + int mlen; + int slen; + unsigned char * data; + }; + +This definition is considered exposed, not opaque (though it is neither +necessary nor recommended that low level maintenance of bstrings be performed +whenever the abstract interfaces are sufficient). The mlen field (usually) +describes a lower bound for the memory allocated for the data field. The +slen field describes the exact length for the bstring. The data field is a +single contiguous buffer of unsigned chars. Note that the existence of a '\0' +character in the unsigned char buffer pointed to by the data field does not +necessarily denote the end of the bstring. + +To be a well formed modifiable bstring the mlen field must be at least the +length of the slen field, and slen must be non-negative. Furthermore, the +data field must point to a valid buffer in which access to the first mlen +characters has been acquired. So the minimal check for correctness is: + + (slen >= 0 && mlen >= slen && data != NULL) + +bstrings returned by bstring functions can be assumed to be either NULL or +satisfy the above property. (When bstrings are only readable, the mlen >= +slen restriction is not required; this is discussed later in this section.) +A bstring itself is just a pointer to a struct tagbstring: + + typedef struct tagbstring * bstring; + +Note that use of the prefix "tag" in struct tagbstring is required to work +around the inconsistency between C and C++'s struct namespace usage. This +definition is also considered exposed. + +Bstrlib basically manages bstrings allocated as a header and an associated +data-buffer. Since the implementation is exposed, they can also be +constructed manually. Functions which mutate bstrings assume that the header +and data buffer have been malloced; the bstring library may perform free() or +realloc() on both the header and data buffer of any bstring parameter. +Functions which return bstring's create new bstrings. The string memory is +freed by a bdestroy() call (or using the bstrFree macro). + +The following related typedef is also provided: + + typedef const struct tagbstring * const_bstring; + +which is also considered exposed. These are directly bstring compatible (no +casting required) but are just used for parameters which are meant to be +non-mutable. So in general, bstring parameters which are read as input but +not meant to be modified will be declared as const_bstring, and bstring +parameters which may be modified will be declared as bstring. This convention +is recommended for user written functions as well. + +Since bstrings maintain interoperability with C library char-buffer style +strings, all functions which modify, update or create bstrings also append a +'\0' character into the position slen + 1. This trailing '\0' character is +not required for bstrings input to the bstring functions; this is provided +solely as a convenience for interoperability with standard C char-buffer +functionality. + +Analogs for the ANSI C string library functions have been created when they +are necessary, but have also been left out when they are not. In particular +there are no functions analogous to fwrite, or puts just for the purposes of +bstring. The ->data member of any string is exposed, and therefore can be +used just as easily as char buffers for C functions which read strings. + +For those that wish to hand construct bstrings, the following should be kept +in mind: + + 1) While bstrlib can accept constructed bstrings without terminating + '\0' characters, the rest of the C language string library will not + function properly on such non-terminated strings. This is obvious + but must be kept in mind. + 2) If it is intended that a constructed bstring be written to by the + bstring library functions then the data portion should be allocated + by the malloc function and the slen and mlen fields should be entered + properly. The struct tagbstring header is not reallocated, and only + freed by bdestroy. + 3) Writing arbitrary '\0' characters at various places in the string + will not modify its length as perceived by the bstring library + functions. In fact, '\0' is a legitimate non-terminating character + for a bstring to contain. + 4) For read only parameters, bstring functions do not check the mlen. + I.e., the minimal correctness requirements are reduced to: + + (slen >= 0 && data != NULL) + +Better pointer arithmetic +------------------------- + +One built-in feature of '\0' terminated char * strings, is that its very easy +and fast to obtain a reference to the tail of any string using pointer +arithmetic. Bstrlib does one better by providing a way to get a reference to +any substring of a bstring (or any other length delimited block of memory.) +So rather than just having pointer arithmetic, with bstrlib one essentially +has segment arithmetic. This is achieved using the macro blk2tbstr() which +builds a reference to a block of memory and the macro bmid2tbstr() which +builds a reference to a segment of a bstring. Bstrlib also includes +functions for direct consumption of memory blocks into bstrings, namely +bcatblk () and blk2bstr (). + +One scenario where this can be extremely useful is when string contains many +substrings which one would like to pass as read-only reference parameters to +some string consuming function without the need to allocate entire new +containers for the string data. More concretely, imagine parsing a command +line string whose parameters are space delimited. This can only be done for +tails of the string with '\0' terminated char * strings. + +Improved NULL semantics and error handling +------------------------------------------ + +Unless otherwise noted, if a NULL pointer is passed as a bstring or any other +detectably illegal parameter, the called function will return with an error +indicator (either NULL or BSTR_ERR) rather than simply performing a NULL +pointer access, or having undefined behavior. + +To illustrate the value of this, consider the following example: + + strcpy (p = malloc (13 * sizeof (char)), "Hello,"); + strcat (p, " World"); + +This is not correct because malloc may return NULL (due to an out of memory +condition), and the behaviour of strcpy is undefined if either of its +parameters are NULL. However: + + bstrcat (p = bfromcstr ("Hello,"), q = bfromcstr (" World")); + bdestroy (q); + +is well defined, because if either p or q are assigned NULL (indicating a +failure to allocate memory) both bstrcat and bdestroy will recognize it and +perform no detrimental action. + +Note that it is not necessary to check any of the members of a returned +bstring for internal correctness (in particular the data member does not need +to be checked against NULL when the header is non-NULL), since this is +assured by the bstring library itself. + +bStreams +-------- + +In addition to the bgets and bread functions, bstrlib can abstract streams +with a high performance read only stream called a bStream. In general, the +idea is to open a core stream (with something like fopen) then pass its +handle as well as a bNread function pointer (like fread) to the bsopen +function which will return a handle to an open bStream. Then the functions +bsread, bsreadln or bsreadlns can be called to read portions of the stream. +Finally, the bsclose function is called to close the bStream -- it will +return a handle to the original (core) stream. So bStreams, essentially, +wrap other streams. + +The bStreams have two main advantages over the bgets and bread (as well as +fgets/ungetc) paradigms: + +1) Improved functionality via the bunread function which allows a stream to + unread characters, giving the bStream stack-like functionality if so + desired. +2) A very high performance bsreadln function. The C library function fgets() + (and the bgets function) can typically be written as a loop on top of + fgetc(), thus paying all of the overhead costs of calling fgetc on a per + character basis. bsreadln will read blocks at a time, thus amortizing the + overhead of fread calls over many characters at once. + +However, clearly bStreams are suboptimal or unusable for certain kinds of +streams (stdin) or certain usage patterns (a few spotty, or non-sequential +reads from a slow stream.) For those situations, using bgets will be more +appropriate. + +The semantics of bStreams allows practical construction of layerable data +streams. What this means is that by writing a bNread compatible function on +top of a bStream, one can construct a new bStream on top of it. This can be +useful for writing multi-pass parsers that don't actually read the entire +input more than once and don't require the use of intermediate storage. + +Aliasing +-------- + +Aliasing occurs when a function is given two parameters which point to data +structures which overlap in the memory they occupy. While this does not +disturb read only functions, for many libraries this can make functions that +write to these memory locations malfunction. This is a common problem of the +C standard library and especially the string functions in the C standard +library. + +The C standard string library is entirely char by char oriented (as is +bstring) which makes conforming implementations alias safe for some +scenarios. However no actual detection of aliasing is typically performed, +so it is easy to find cases where the aliasing will cause anomolous or +undesirable behaviour (consider: strcat (p, p).) The C99 standard includes +the "restrict" pointer modifier which allows the compiler to document and +assume a no-alias condition on usage. However, only the most trivial cases +can be caught (if at all) by the compiler at compile time, and thus there is +no actual enforcement of non-aliasing. + +Bstrlib, by contrast, permits aliasing and is completely aliasing safe, in +the C99 sense of aliasing. That is to say, under the assumption that +pointers of incompatible types from distinct objects can never alias, bstrlib +is completely aliasing safe. (In practice this means that the data buffer +portion of any bstring and header of any bstring are assumed to never alias.) +With the exception of the reference building macros, the library behaves as +if all read-only parameters are first copied and replaced by temporary +non-aliased parameters before any writing to any output bstring is performed +(though actual copying is extremely rarely ever done.) + +Besides being a useful safety feature, bstring searching/comparison +functions can improve to O(1) execution when aliasing is detected. + +Note that aliasing detection and handling code in Bstrlib is generally +extremely cheap. There is almost never any appreciable performance penalty +for using aliased parameters. + +Reenterancy +----------- + +Nearly every function in Bstrlib is a leaf function, and is completely +reenterable with the exception of writing to common bstrings. The split +functions which use a callback mechanism requires only that the source string +not be destroyed by the callback function unless the callback function returns +with an error status (note that Bstrlib functions which return an error do +not modify the string in any way.) The string can in fact be modified by the +callback and the behaviour is deterministic. See the documentation of the +various split functions for more details. + +Undefined scenarios +------------------- + +One of the basic important premises for Bstrlib is to not to increase the +propogation of undefined situations from parameters that are otherwise legal +in of themselves. In particular, except for extremely marginal cases, usages +of bstrings that use the bstring library functions alone cannot lead to any +undefined action. But due to C/C++ language and library limitations, there +is no way to define a non-trivial library that is completely without +undefined operations. All such possible undefined operations are described +below: + +1) bstrings or struct tagbstrings that are not explicitely initialized cannot + be passed as a parameter to any bstring function. +2) The members of the NULL bstring cannot be accessed directly. (Though all + APIs and macros detect the NULL bstring.) +3) A bstring whose data member has not been obtained from a malloc or + compatible call and which is write accessible passed as a writable + parameter will lead to undefined results. (i.e., do not writeAllow any + constructed bstrings unless the data portion has been obtained from the + heap.) +4) If the headers of two strings alias but are not identical (which can only + happen via a defective manual construction), then passing them to a + bstring function in which one is writable is not defined. +5) If the mlen member is larger than the actual accessible length of the data + member for a writable bstring, or if the slen member is larger than the + readable length of the data member for a readable bstring, then the + corresponding bstring operations are undefined. +6) Any bstring definition whose header or accessible data portion has been + assigned to inaccessible or otherwise illegal memory clearly cannot be + acted upon by the bstring library in any way. +7) Destroying the source of an incremental split from within the callback + and not returning with a negative value (indicating that it should abort) + will lead to undefined behaviour. (Though *modifying* or adjusting the + state of the source data, even if those modification fail within the + bstrlib API, has well defined behavior.) +8) Modifying a bstring which is write protected by direct access has + undefined behavior. + +While this may seem like a long list, with the exception of invalid uses of +the writeAllow macro, and source destruction during an iterative split +without an accompanying abort, no usage of the bstring API alone can cause +any undefined scenario to occurr. I.e., the policy of restricting usage of +bstrings to the bstring API can significantly reduce the risk of runtime +errors (in practice it should eliminate them) related to string manipulation +due to undefined action. + +C++ wrapper +----------- + +A C++ wrapper has been created to enable bstring functionality for C++ in the +most natural (for C++ programers) way possible. The mandate for the C++ +wrapper is different from the base C bstring library. Since the C++ language +has far more abstracting capabilities, the CBString structure is considered +fully abstracted -- i.e., hand generated CBStrings are not supported (though +conversion from a struct tagbstring is allowed) and all detectable errors are +manifest as thrown exceptions. + +- The C++ class definitions are all under the namespace Bstrlib. bstrwrap.h + enables this namespace (with a using namespace Bstrlib; directive at the + end) unless the macro BSTRLIB_DONT_ASSUME_NAMESPACE has been defined before + it is included. + +- Erroneous accesses results in an exception being thrown. The exception + parameter is of type "struct CBStringException" which is derived from + std::exception if STL is used. A verbose description of the error message + can be obtained from the what() method. + +- CBString is a C++ structure derived from a struct tagbstring. An address + of a CBString cast to a bstring must not be passed to bdestroy. The bstring + C API has been made C++ safe and can be used directly in a C++ project. + +- It includes constructors which can take a char, '\0' terminated char + buffer, tagbstring, (char, repeat-value), a length delimited buffer or a + CBStringList to initialize it. + +- Concatenation is performed with the + and += operators. Comparisons are + done with the ==, !=, <, >, <= and >= operators. Note that == and != use + the biseq call, while <, >, <= and >= use bstrcmp. + +- CBString's can be directly cast to const character buffers. + +- CBString's can be directly cast to double, float, int or unsigned int so + long as the CBString are decimal representations of those types (otherwise + an exception will be thrown). Converting the other way should be done with + the format(a) method(s). + +- CBString contains the length, character and [] accessor methods. The + character and [] accessors are aliases of each other. If the bounds for + the string are exceeded, an exception is thrown. To avoid the overhead for + this check, first cast the CBString to a (const char *) and use [] to + dereference the array as normal. Note that the character and [] accessor + methods allows both reading and writing of individual characters. + +- The methods: format, formata, find, reversefind, findcaseless, + reversefindcaseless, midstr, insert, insertchrs, replace, findreplace, + findreplacecaseless, remove, findchr, nfindchr, alloc, toupper, tolower, + gets, read are analogous to the functions that can be found in the C API. + +- The caselessEqual and caselessCmp methods are analogous to biseqcaseless + and bstricmp functions respectively. + +- Note that just like the bformat function, the format and formata methods do + not automatically cast CBStrings into char * strings for "%s"-type + substitutions: + + CBString w("world"); + CBString h("Hello"); + CBString hw; + + /* The casts are necessary */ + hw.format ("%s, %s", (const char *)h, (const char *)w); + +- The methods trunc and repeat have been added instead of using pattern. + +- ltrim, rtrim and trim methods have been added. These remove characters + from a given character string set (defaulting to the whitespace characters) + from either the left, right or both ends of the CBString, respectively. + +- The method setsubstr is also analogous in functionality to bsetstr, except + that it cannot be passed NULL. Instead the method fill and the fill-style + constructor have been supplied to enable this functionality. + +- The writeprotect(), writeallow() and iswriteprotected() methods are + analogous to the bwriteprotect(), bwriteallow() and biswriteprotected() + macros in the C API. Write protection semantics in CBString are stronger + than with the C API in that indexed character assignment is checked for + write protection. However, unlike with the C API, a write protected + CBString can be destroyed by the destructor. + +- CBStream is a C++ structure which wraps a struct bStream (its not derived + from it, since destruction is slightly different). It is constructed by + passing in a bNread function pointer and a stream parameter cast to void *. + This structure includes methods for detecting eof, setting the buffer + length, reading the whole stream or reading entries line by line or block + by block, an unread function, and a peek function. + +- If STL is available, the CBStringList structure is derived from a vector of + CBString with various split methods. The split method has been overloaded + to accept either a character or CBString as the second parameter (when the + split parameter is a CBString any character in that CBString is used as a + seperator). The splitstr method takes a CBString as a substring seperator. + Joins can be performed via a CBString constructor which takes a + CBStringList as a parameter, or just using the CBString::join() method. + +- If there is proper support for std::iostreams, then the >> and << operators + and the getline() function have been added (with semantics the same as + those for std::string). + +Multithreading +-------------- + +A mutable bstring is kind of analogous to a small (two entry) linked list +allocated by malloc, with all aliasing completely under programmer control. +I.e., manipulation of one bstring will never affect any other distinct +bstring unless explicitely constructed to do so by the programmer via hand +construction or via building a reference. Bstrlib also does not use any +static or global storage, so there are no hidden unremovable race conditions. +Bstrings are also clearly not inherently thread local. So just like +char *'s, bstrings can be passed around from thread to thread and shared and +so on, so long as modifications to a bstring correspond to some kind of +exclusive access lock as should be expected (or if the bstring is read-only, +which can be enforced by bstring write protection) for any sort of shared +object in a multithreaded environment. + +Bsafe module +------------ + +For convenience, a bsafe module has been included. The idea is that if this +module is included, inadvertant usage of the most dangerous C functions will +be overridden and lead to an immediate run time abort. Of course, it should +be emphasized that usage of this module is completely optional. The +intention is essentially to provide an option for creating project safety +rules which can be enforced mechanically rather than socially. This is +useful for larger, or open development projects where its more difficult to +enforce social rules or "coding conventions". + +Problems not solved +------------------- + +Bstrlib is written for the C and C++ languages, which have inherent weaknesses +that cannot be easily solved: + +1. Memory leaks: Forgetting to call bdestroy on a bstring that is about to be + unreferenced, just as forgetting to call free on a heap buffer that is + about to be dereferenced. Though bstrlib itself is leak free. +2. Read before write usage: In C, declaring an auto bstring does not + automatically fill it with legal/valid contents. This problem has been + somewhat mitigated in C++. (The bstrDeclare and bstrFree macros from + bstraux can be used to help mitigate this problem.) + +Other problems not addressed: + +3. Built-in mutex usage to automatically avoid all bstring internal race + conditions in multitasking environments: The problem with trying to + implement such things at this low a level is that it is typically more + efficient to use locks in higher level primitives. There is also no + platform independent way to implement locks or mutexes. +4. Unicode/widecharacter support. + +Note that except for spotty support of wide characters, the default C +standard library does not address any of these problems either. + +Configurable compilation options +-------------------------------- + +All configuration options are meant solely for the purpose of compiler +compatibility. Configuration options are not meant to change the semantics +or capabilities of the library, except where it is unavoidable. + +Since some C++ compilers don't include the Standard Template Library and some +have the options of disabling exception handling, a number of macros can be +used to conditionally compile support for each of this: + +BSTRLIB_CAN_USE_STL + + - defining this will enable the used of the Standard Template Library. + Defining BSTRLIB_CAN_USE_STL overrides the BSTRLIB_CANNOT_USE_STL macro. + +BSTRLIB_CANNOT_USE_STL + + - defining this will disable the use of the Standard Template Library. + Defining BSTRLIB_CAN_USE_STL overrides the BSTRLIB_CANNOT_USE_STL macro. + +BSTRLIB_CAN_USE_IOSTREAM + + - defining this will enable the used of streams from class std. Defining + BSTRLIB_CAN_USE_IOSTREAM overrides the BSTRLIB_CANNOT_USE_IOSTREAM macro. + +BSTRLIB_CANNOT_USE_IOSTREAM + + - defining this will disable the use of streams from class std. Defining + BSTRLIB_CAN_USE_IOSTREAM overrides the BSTRLIB_CANNOT_USE_IOSTREAM macro. + +BSTRLIB_THROWS_EXCEPTIONS + + - defining this will enable the exception handling within bstring. + Defining BSTRLIB_THROWS_EXCEPTIONS overrides the + BSTRLIB_DOESNT_THROWS_EXCEPTIONS macro. + +BSTRLIB_DOESNT_THROW_EXCEPTIONS + + - defining this will disable the exception handling within bstring. + Defining BSTRLIB_THROWS_EXCEPTIONS overrides the + BSTRLIB_DOESNT_THROW_EXCEPTIONS macro. + +Note that these macros must be defined consistently throughout all modules +that use CBStrings including bstrwrap.cpp. + +Some older C compilers do not support functions such as vsnprintf. This is +handled by the following macro variables: + +BSTRLIB_NOVSNP + + - defining this indicates that the compiler does not support vsnprintf. + This will cause bformat and bformata to not be declared. Note that + for some compilers, such as Turbo C, this is set automatically. + Defining BSTRLIB_NOVSNP overrides the BSTRLIB_VSNP_OK macro. + +BSTRLIB_VSNP_OK + + - defining this will disable the autodetection of compilers the do not + support of compilers that do not support vsnprintf. + Defining BSTRLIB_NOVSNP overrides the BSTRLIB_VSNP_OK macro. + +Semantic compilation options +---------------------------- + +Bstrlib comes with very few compilation options for changing the semantics of +of the library. These are described below. + +BSTRLIB_DONT_ASSUME_NAMESPACE + + - Defining this before including bstrwrap.h will disable the automatic + enabling of the Bstrlib namespace for the C++ declarations. + +BSTRLIB_DONT_USE_VIRTUAL_DESTRUCTOR + + - Defining this will make the CBString destructor non-virtual. + +BSTRLIB_MEMORY_DEBUG + + - Defining this will cause the bstrlib modules bstrlib.c and bstrwrap.cpp + to invoke a #include "memdbg.h". memdbg.h has to be supplied by the user. + +Note that these macros must be defined consistently throughout all modules +that use bstrings or CBStrings including bstrlib.c, bstraux.c and +bstrwrap.cpp. + +=============================================================================== + +Files +----- + +bstrlib.c - C implementaion of bstring functions. +bstrlib.h - C header file for bstring functions. +bstraux.c - C example that implements trivial additional functions. +bstraux.h - C header for bstraux.c +bstest.c - C unit/regression test for bstrlib.c + +bstrwrap.cpp - C++ implementation of CBString. +bstrwrap.h - C++ header file for CBString. +test.cpp - C++ unit/regression test for bstrwrap.cpp + +bsafe.c - C runtime stubs to abort usage of unsafe C functions. +bsafe.h - C header file for bsafe.c functions. + +C projects need only include bstrlib.h and compile/link bstrlib.c to use the +bstring library. C++ projects need to additionally include bstrwrap.h and +compile/link bstrwrap.cpp. For both, there may be a need to make choices +about feature configuration as described in the "Configurable compilation +options" in the section above. + +Other files that are included in this archive are: + +license.txt - The 3 clause BSD license for Bstrlib +gpl.txt - The GPL version 2 +security.txt - A security statement useful for auditting Bstrlib +porting.txt - A guide to porting Bstrlib +bstrlib.txt - This file + +=============================================================================== + +The functions +------------- + + extern bstring bfromcstr (const char * str); + + Take a standard C library style '\0' terminated char buffer and generate + a bstring with the same contents as the char buffer. If an error occurs + NULL is returned. + + So for example: + + bstring b = bfromcstr ("Hello"); + if (!b) { + fprintf (stderr, "Out of memory"); + } else { + puts ((char *) b->data); + } + + .......................................................................... + + extern bstring bfromcstralloc (int mlen, const char * str); + + Create a bstring which contains the contents of the '\0' terminated + char * buffer str. The memory buffer backing the bstring is at least + mlen characters in length. If an error occurs NULL is returned. + + So for example: + + bstring b = bfromcstralloc (64, someCstr); + if (b) b->data[63] = 'x'; + + The idea is that this will set the 64th character of b to 'x' if it is at + least 64 characters long otherwise do nothing. And we know this is well + defined so long as b was successfully created, since it will have been + allocated with at least 64 characters. + + .......................................................................... + + extern bstring blk2bstr (const void * blk, int len); + + Create a bstring whose contents are described by the contiguous buffer + pointing to by blk with a length of len bytes. Note that this function + creates a copy of the data in blk, rather than simply referencing it. + Compare with the blk2tbstr macro. If an error occurs NULL is returned. + + .......................................................................... + + extern char * bstr2cstr (const_bstring s, char z); + + Create a '\0' terminated char buffer which contains the contents of the + bstring s, except that any contained '\0' characters are converted to the + character in z. This returned value should be freed with bcstrfree(), by + the caller. If an error occurs NULL is returned. + + .......................................................................... + + extern int bcstrfree (char * s); + + Frees a C-string generated by bstr2cstr (). This is normally unnecessary + since it just wraps a call to free (), however, if malloc () and free () + have been redefined as a macros within the bstrlib module (via macros in + the memdbg.h backdoor) with some difference in behaviour from the std + library functions, then this allows a correct way of freeing the memory + that allows higher level code to be independent from these macro + redefinitions. + + .......................................................................... + + extern bstring bstrcpy (const_bstring b1); + + Make a copy of the passed in bstring. The copied bstring is returned if + there is no error, otherwise NULL is returned. + + .......................................................................... + + extern int bassign (bstring a, const_bstring b); + + Overwrite the bstring a with the contents of bstring b. Note that the + bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a is not overwritten. + + .......................................................................... + + int bassigncstr (bstring a, const char * str); + + Overwrite the string a with the contents of char * string str. Note that + the bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a may be partially overwritten. + + .......................................................................... + + int bassignblk (bstring a, const void * s, int len); + + Overwrite the string a with the contents of the block (s, len). Note that + the bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a is not overwritten. + + .......................................................................... + + extern int bassignmidstr (bstring a, const_bstring b, int left, int len); + + Overwrite the bstring a with the middle of contents of bstring b + starting from position left and running for a length len. left and + len are clamped to the ends of b as with the function bmidstr. Note that + the bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a is not overwritten. + + .......................................................................... + + extern bstring bmidstr (const_bstring b, int left, int len); + + Create a bstring which is the substring of b starting from position left + and running for a length len (clamped by the end of the bstring b.) If + there was no error, the value of this constructed bstring is returned + otherwise NULL is returned. + + .......................................................................... + + extern int bdelete (bstring s1, int pos, int len); + + Removes characters from pos to pos+len-1 and shifts the tail of the + bstring starting from pos+len to pos. len must be positive for this call + to have any effect. The section of the bstring described by (pos, len) + is clamped to boundaries of the bstring b. The value BSTR_OK is returned + if the operation is successful, otherwise BSTR_ERR is returned. + + .......................................................................... + + extern int bconcat (bstring b0, const_bstring b1); + + Concatenate the bstring b1 to the end of bstring b0. The value BSTR_OK + is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int bconchar (bstring b, char c); + + Concatenate the character c to the end of bstring b. The value BSTR_OK + is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int bcatcstr (bstring b, const char * s); + + Concatenate the char * string s to the end of bstring b. The value + BSTR_OK is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int bcatblk (bstring b, const void * s, int len); + + Concatenate a fixed length buffer (s, len) to the end of bstring b. The + value BSTR_OK is returned if the operation is successful, otherwise + BSTR_ERR is returned. + + .......................................................................... + + extern int biseq (const_bstring b0, const_bstring b1); + + Compare the bstring b0 and b1 for equality. If the bstrings differ, 0 + is returned, if the bstrings are the same, 1 is returned, if there is an + error, -1 is returned. If the length of the bstrings are different, this + function has O(1) complexity. Contained '\0' characters are not treated + as a termination character. + + Note that the semantics of biseq are not completely compatible with + bstrcmp because of its different treatment of the '\0' character. + + .......................................................................... + + extern int bisstemeqblk (const_bstring b, const void * blk, int len); + + Compare beginning of bstring b0 with a block of memory of length len for + equality. If the beginning of b0 differs from the memory block (or if b0 + is too short), 0 is returned, if the bstrings are the same, 1 is returned, + if there is an error, -1 is returned. + + .......................................................................... + + extern int biseqcaseless (const_bstring b0, const_bstring b1); + + Compare two bstrings for equality without differentiating between case. + If the bstrings differ other than in case, 0 is returned, if the bstrings + are the same, 1 is returned, if there is an error, -1 is returned. If + the length of the bstrings are different, this function is O(1). '\0' + termination characters are not treated in any special way. + + .......................................................................... + + extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len); + + Compare beginning of bstring b0 with a block of memory of length len + without differentiating between case for equality. If the beginning of b0 + differs from the memory block other than in case (or if b0 is too short), + 0 is returned, if the bstrings are the same, 1 is returned, if there is an + error, -1 is returned. + + .......................................................................... + + extern int biseqcstr (const_bstring b, const char *s); + + Compare the bstring b and char * bstring s. The C string s must be '\0' + terminated at exactly the length of the bstring b, and the contents + between the two must be identical with the bstring b with no '\0' + characters for the two contents to be considered equal. This is + equivalent to the condition that their current contents will be always be + equal when comparing them in the same format after converting one or the + other. If they are equal 1 is returned, if they are unequal 0 is + returned and if there is a detectable error BSTR_ERR is returned. + + .......................................................................... + + extern int biseqcstrcaseless (const_bstring b, const char *s); + + Compare the bstring b and char * string s. The C string s must be '\0' + terminated at exactly the length of the bstring b, and the contents + between the two must be identical except for case with the bstring b with + no '\0' characters for the two contents to be considered equal. This is + equivalent to the condition that their current contents will be always be + equal ignoring case when comparing them in the same format after + converting one or the other. If they are equal, except for case, 1 is + returned, if they are unequal regardless of case 0 is returned and if + there is a detectable error BSTR_ERR is returned. + + .......................................................................... + + extern int bstrcmp (const_bstring b0, const_bstring b1); + + Compare the bstrings b0 and b1 for ordering. If there is an error, + SHRT_MIN is returned, otherwise a value less than or greater than zero, + indicating that the bstring pointed to by b0 is lexicographically less + than or greater than the bstring pointed to by b1 is returned. If the + bstring lengths are unequal but the characters up until the length of the + shorter are equal then a value less than, or greater than zero, + indicating that the bstring pointed to by b0 is shorter or longer than the + bstring pointed to by b1 is returned. 0 is returned if and only if the + two bstrings are the same. If the length of the bstrings are different, + this function is O(n). Like its standard C library counter part, the + comparison does not proceed past any '\0' termination characters + encountered. + + The seemingly odd error return value, merely provides slightly more + granularity than the undefined situation given in the C library function + strcmp. The function otherwise behaves very much like strcmp(). + + Note that the semantics of bstrcmp are not completely compatible with + biseq because of its different treatment of the '\0' termination + character. + + .......................................................................... + + extern int bstrncmp (const_bstring b0, const_bstring b1, int n); + + Compare the bstrings b0 and b1 for ordering for at most n characters. If + there is an error, SHRT_MIN is returned, otherwise a value is returned as + if b0 and b1 were first truncated to at most n characters then bstrcmp + was called with these new bstrings are paremeters. If the length of the + bstrings are different, this function is O(n). Like its standard C + library counter part, the comparison does not proceed past any '\0' + termination characters encountered. + + The seemingly odd error return value, merely provides slightly more + granularity than the undefined situation given in the C library function + strncmp. The function otherwise behaves very much like strncmp(). + + .......................................................................... + + extern int bstricmp (const_bstring b0, const_bstring b1); + + Compare two bstrings without differentiating between case. The return + value is the difference of the values of the characters where the two + bstrings first differ, otherwise 0 is returned indicating that the + bstrings are equal. If the lengths are different, then a difference from + 0 is given, but if the first extra character is '\0', then it is taken to + be the value UCHAR_MAX+1. + + .......................................................................... + + extern int bstrnicmp (const_bstring b0, const_bstring b1, int n); + + Compare two bstrings without differentiating between case for at most n + characters. If the position where the two bstrings first differ is + before the nth position, the return value is the difference of the values + of the characters, otherwise 0 is returned. If the lengths are different + and less than n characters, then a difference from 0 is given, but if the + first extra character is '\0', then it is taken to be the value + UCHAR_MAX+1. + + .......................................................................... + + extern int bdestroy (bstring b); + + Deallocate the bstring passed. Passing NULL in as a parameter will have + no effect. Note that both the header and the data portion of the bstring + will be freed. No other bstring function which modifies one of its + parameters will free or reallocate the header. Because of this, in + general, bdestroy cannot be called on any declared struct tagbstring even + if it is not write protected. A bstring which is write protected cannot + be destroyed via the bdestroy call. Any attempt to do so will result in + no action taken, and BSTR_ERR will be returned. + + Note to C++ users: Passing in a CBString cast to a bstring will lead to + undefined behavior (free will be called on the header, rather than the + CBString destructor.) Instead just use the ordinary C++ language + facilities to dealloc a CBString. + + .......................................................................... + + extern int binstr (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + forward (increasing) direction. If it is found then it returns with the + first position after pos where it is found, otherwise it returns BSTR_ERR. + The algorithm used is brute force; O(m*n). + + .......................................................................... + + extern int binstrr (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + backward (decreasing) direction. If it is found then it returns with the + first position after pos where it is found, otherwise return BSTR_ERR. + Note that the current position at pos is tested as well -- so to be + disjoint from a previous forward search it is recommended that the + position be backed up (decremented) by one position. The algorithm used + is brute force; O(m*n). + + .......................................................................... + + extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + forward (increasing) direction but without regard to case. If it is + found then it returns with the first position after pos where it is + found, otherwise it returns BSTR_ERR. The algorithm used is brute force; + O(m*n). + + .......................................................................... + + extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + backward (decreasing) direction but without regard to case. If it is + found then it returns with the first position after pos where it is + found, otherwise return BSTR_ERR. Note that the current position at pos + is tested as well -- so to be disjoint from a previous forward search it + is recommended that the position be backed up (decremented) by one + position. The algorithm used is brute force; O(m*n). + + .......................................................................... + + extern int binchr (const_bstring b0, int pos, const_bstring b1); + + Search for the first position in b0 starting from pos or after, in which + one of the characters in b1 is found. This function has an execution + time of O(b0->slen + b1->slen). If such a position does not exist in b0, + then BSTR_ERR is returned. + + .......................................................................... + + extern int binchrr (const_bstring b0, int pos, const_bstring b1); + + Search for the last position in b0 no greater than pos, in which one of + the characters in b1 is found. This function has an execution time + of O(b0->slen + b1->slen). If such a position does not exist in b0, + then BSTR_ERR is returned. + + .......................................................................... + + extern int bninchr (const_bstring b0, int pos, const_bstring b1); + + Search for the first position in b0 starting from pos or after, in which + none of the characters in b1 is found and return it. This function has + an execution time of O(b0->slen + b1->slen). If such a position does + not exist in b0, then BSTR_ERR is returned. + + .......................................................................... + + extern int bninchrr (const_bstring b0, int pos, const_bstring b1); + + Search for the last position in b0 no greater than pos, in which none of + the characters in b1 is found and return it. This function has an + execution time of O(b0->slen + b1->slen). If such a position does not + exist in b0, then BSTR_ERR is returned. + + .......................................................................... + + extern int bstrchr (const_bstring b, int c); + + Search for the character c in the bstring b forwards from the start of + the bstring. Returns the position of the found character or BSTR_ERR if + it is not found. + + NOTE: This has been implemented as a macro on top of bstrchrp (). + + .......................................................................... + + extern int bstrrchr (const_bstring b, int c); + + Search for the character c in the bstring b backwards from the end of the + bstring. Returns the position of the found character or BSTR_ERR if it is + not found. + + NOTE: This has been implemented as a macro on top of bstrrchrp (). + + .......................................................................... + + extern int bstrchrp (const_bstring b, int c, int pos); + + Search for the character c in b forwards from the position pos + (inclusive). Returns the position of the found character or BSTR_ERR if + it is not found. + + .......................................................................... + + extern int bstrrchrp (const_bstring b, int c, int pos); + + Search for the character c in b backwards from the position pos in bstring + (inclusive). Returns the position of the found character or BSTR_ERR if + it is not found. + + .......................................................................... + + extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill); + + Overwrite the bstring b0 starting at position pos with the bstring b1. If + the position pos is past the end of b0, then the character "fill" is + appended as necessary to make up the gap between the end of b0 and pos. + If b1 is NULL, it behaves as if it were a 0-length bstring. The value + BSTR_OK is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill); + + Inserts the bstring s2 into s1 at position pos. If the position pos is + past the end of s1, then the character "fill" is appended as necessary to + make up the gap between the end of s1 and pos. The value BSTR_OK is + returned if the operation is successful, otherwise BSTR_ERR is returned. + + .......................................................................... + + extern int binsertch (bstring s1, int pos, int len, unsigned char fill); + + Inserts the character fill repeatedly into s1 at position pos for a + length len. If the position pos is past the end of s1, then the + character "fill" is appended as necessary to make up the gap between the + end of s1 and the position pos + len (exclusive). The value BSTR_OK is + returned if the operation is successful, otherwise BSTR_ERR is returned. + + .......................................................................... + + extern int breplace (bstring b1, int pos, int len, const_bstring b2, + unsigned char fill); + + Replace a section of a bstring from pos for a length len with the bstring + b2. If the position pos is past the end of b1 then the character "fill" + is appended as necessary to make up the gap between the end of b1 and + pos. + + .......................................................................... + + extern int bfindreplace (bstring b, const_bstring find, + const_bstring replace, int position); + + Replace all occurrences of the find substring with a replace bstring + after a given position in the bstring b. The find bstring must have a + length > 0 otherwise BSTR_ERR is returned. This function does not + perform recursive per character replacement; that is to say successive + searches resume at the position after the last replace. + + So for example: + + bfindreplace (a0 = bfromcstr("aabaAb"), a1 = bfromcstr("a"), + a2 = bfromcstr("aa"), 0); + + Should result in changing a0 to "aaaabaaAb". + + This function performs exactly (b->slen - position) bstring comparisons, + and data movement is bounded above by character volume equivalent to size + of the output bstring. + + .......................................................................... + + extern int bfindreplacecaseless (bstring b, const_bstring find, + const_bstring replace, int position); + + Replace all occurrences of the find substring, ignoring case, with a + replace bstring after a given position in the bstring b. The find bstring + must have a length > 0 otherwise BSTR_ERR is returned. This function + does not perform recursive per character replacement; that is to say + successive searches resume at the position after the last replace. + + So for example: + + bfindreplacecaseless (a0 = bfromcstr("AAbaAb"), a1 = bfromcstr("a"), + a2 = bfromcstr("aa"), 0); + + Should result in changing a0 to "aaaabaaaab". + + This function performs exactly (b->slen - position) bstring comparisons, + and data movement is bounded above by character volume equivalent to size + of the output bstring. + + .......................................................................... + + extern int balloc (bstring b, int length); + + Increase the allocated memory backing the data buffer for the bstring b + to a length of at least length. If the memory backing the bstring b is + already large enough, not action is performed. This has no effect on the + bstring b that is visible to the bstring API. Usually this function will + only be used when a minimum buffer size is required coupled with a direct + access to the ->data member of the bstring structure. + + Be warned that like any other bstring function, the bstring must be well + defined upon entry to this function. I.e., doing something like: + + b->slen *= 2; /* ?? Most likely incorrect */ + balloc (b, b->slen); + + is invalid, and should be implemented as: + + int t; + if (BSTR_OK == balloc (b, t = (b->slen * 2))) b->slen = t; + + This function will return with BSTR_ERR if b is not detected as a valid + bstring or length is not greater than 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int ballocmin (bstring b, int length); + + Change the amount of memory backing the bstring b to at least length. + This operation will never truncate the bstring data including the + extra terminating '\0' and thus will not decrease the length to less than + b->slen + 1. Note that repeated use of this function may cause + performance problems (realloc may be called on the bstring more than + the O(log(INT_MAX)) times). This function will return with BSTR_ERR if b + is not detected as a valid bstring or length is not greater than 0, + otherwise BSTR_OK is returned. + + So for example: + + if (BSTR_OK == ballocmin (b, 64)) b->data[63] = 'x'; + + The idea is that this will set the 64th character of b to 'x' if it is at + least 64 characters long otherwise do nothing. And we know this is well + defined so long as the ballocmin call was successfully, since it will + ensure that b has been allocated with at least 64 characters. + + .......................................................................... + + int btrunc (bstring b, int n); + + Truncate the bstring to at most n characters. This function will return + with BSTR_ERR if b is not detected as a valid bstring or n is less than + 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int bpattern (bstring b, int len); + + Replicate the starting bstring, b, end to end repeatedly until it + surpasses len characters, then chop the result to exactly len characters. + This function operates in-place. This function will return with BSTR_ERR + if b is NULL or of length 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int btoupper (bstring b); + + Convert contents of bstring to upper case. This function will return with + BSTR_ERR if b is NULL or of length 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int btolower (bstring b); + + Convert contents of bstring to lower case. This function will return with + BSTR_ERR if b is NULL or of length 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int bltrimws (bstring b); + + Delete whitespace contiguous from the left end of the bstring. This + function will return with BSTR_ERR if b is NULL or of length 0, otherwise + BSTR_OK is returned. + + .......................................................................... + + extern int brtrimws (bstring b); + + Delete whitespace contiguous from the right end of the bstring. This + function will return with BSTR_ERR if b is NULL or of length 0, otherwise + BSTR_OK is returned. + + .......................................................................... + + extern int btrimws (bstring b); + + Delete whitespace contiguous from both ends of the bstring. This function + will return with BSTR_ERR if b is NULL or of length 0, otherwise BSTR_OK + is returned. + + .......................................................................... + + extern int bstrListCreate (void); + + Create an empty struct bstrList. The struct bstrList output structure is + declared as follows: + + struct bstrList { + int qty, mlen; + bstring * entry; + }; + + The entry field actually is an array with qty number entries. The mlen + record counts the maximum number of bstring's for which there is memory + in the entry record. + + The Bstrlib API does *NOT* include a comprehensive set of functions for + full management of struct bstrList in an abstracted way. The reason for + this is because aliasing semantics of the list are best left to the user + of this function, and performance varies wildly depending on the + assumptions made. For a complete list of bstring data type it is + recommended that the C++ public std::vector be used, since its + semantics are usage are more standard. + + .......................................................................... + + extern int bstrListDestroy (struct bstrList * sl); + + Destroy a struct bstrList structure that was returned by the bsplit + function. Note that this will destroy each bstring in the ->entry array + as well. See bstrListCreate() above for structure of struct bstrList. + + .......................................................................... + + extern int bstrListAlloc (struct bstrList * sl, int msz); + + Ensure that there is memory for at least msz number of entries for the + list. + + .......................................................................... + + extern int bstrListAllocMin (struct bstrList * sl, int msz); + + Try to allocate the minimum amount of memory for the list to include at + least msz entries or sl->qty whichever is greater. + + .......................................................................... + + extern struct bstrList * bsplit (bstring str, unsigned char splitChar); + + Create an array of sequential substrings from str divided by the + character splitChar. Successive occurrences of the splitChar will be + divided by empty bstring entries, following the semantics from the Python + programming language. To reclaim the memory from this output structure, + bstrListDestroy () should be called. See bstrListCreate() above for + structure of struct bstrList. + + .......................................................................... + + extern struct bstrList * bsplits (bstring str, const_bstring splitStr); + + Create an array of sequential substrings from str divided by any + character contained in splitStr. An empty splitStr causes a single entry + bstrList containing a copy of str to be returned. See bstrListCreate() + above for structure of struct bstrList. + + .......................................................................... + + extern struct bstrList * bsplitstr (bstring str, const_bstring splitStr); + + Create an array of sequential substrings from str divided by the entire + substring splitStr. An empty splitStr causes a single entry bstrList + containing a copy of str to be returned. See bstrListCreate() above for + structure of struct bstrList. + + .......................................................................... + + extern bstring bjoin (const struct bstrList * bl, const_bstring sep); + + Join the entries of a bstrList into one bstring by sequentially + concatenating them with the sep bstring in between. If sep is NULL, it + is treated as if it were the empty bstring. Note that: + + bjoin (l = bsplit (b, s->data[0]), s); + + should result in a copy of b, if s->slen is 1. If there is an error NULL + is returned, otherwise a bstring with the correct result is returned. + See bstrListCreate() above for structure of struct bstrList. + + .......................................................................... + + extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + + Iterate the set of disjoint sequential substrings over str starting at + position pos divided by the character splitChar. The parm passed to + bsplitcb is passed on to cb. If the function cb returns a value < 0, + then further iterating is halted and this value is returned by bsplitcb. + + Note: Non-destructive modification of str from within the cb function + while performing this split is not undefined. bsplitcb behaves in + sequential lock step with calls to cb. I.e., after returning from a cb + that return a non-negative integer, bsplitcb continues from the position + 1 character after the last detected split character and it will halt + immediately if the length of str falls below this point. However, if the + cb function destroys str, then it *must* return with a negative value, + otherwise bsplitcb will continue in an undefined manner. + + This function is provided as an incremental alternative to bsplit that is + abortable and which does not impose additional memory allocation. + + .......................................................................... + + extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + + Iterate the set of disjoint sequential substrings over str starting at + position pos divided by any of the characters in splitStr. An empty + splitStr causes the whole str to be iterated once. The parm passed to + bsplitcb is passed on to cb. If the function cb returns a value < 0, + then further iterating is halted and this value is returned by bsplitcb. + + Note: Non-destructive modification of str from within the cb function + while performing this split is not undefined. bsplitscb behaves in + sequential lock step with calls to cb. I.e., after returning from a cb + that return a non-negative integer, bsplitscb continues from the position + 1 character after the last detected split character and it will halt + immediately if the length of str falls below this point. However, if the + cb function destroys str, then it *must* return with a negative value, + otherwise bsplitscb will continue in an undefined manner. + + This function is provided as an incremental alternative to bsplits that + is abortable and which does not impose additional memory allocation. + + .......................................................................... + + extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + + Iterate the set of disjoint sequential substrings over str starting at + position pos divided by the entire substring splitStr. An empty splitStr + causes each character of str to be iterated. The parm passed to bsplitcb + is passed on to cb. If the function cb returns a value < 0, then further + iterating is halted and this value is returned by bsplitcb. + + Note: Non-destructive modification of str from within the cb function + while performing this split is not undefined. bsplitstrcb behaves in + sequential lock step with calls to cb. I.e., after returning from a cb + that return a non-negative integer, bsplitstrcb continues from the position + 1 character after the last detected split character and it will halt + immediately if the length of str falls below this point. However, if the + cb function destroys str, then it *must* return with a negative value, + otherwise bsplitscb will continue in an undefined manner. + + This function is provided as an incremental alternative to bsplitstr that + is abortable and which does not impose additional memory allocation. + + .......................................................................... + + extern bstring bformat (const char * fmt, ...); + + Takes the same parameters as printf (), but rather than outputting + results to stdio, it forms a bstring which contains what would have been + output. Note that if there is an early generation of a '\0' character, + the bstring will be truncated to this end point. + + Note that %s format tokens correspond to '\0' terminated char * buffers, + not bstrings. To print a bstring, first dereference data element of the + the bstring: + + /* b1->data needs to be '\0' terminated, so tagbstrings generated + by blk2tbstr () might not be suitable. */ + b0 = bformat ("Hello, %s", b1->data); + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bformat function is not present. + + .......................................................................... + + extern int bformata (bstring b, const char * fmt, ...); + + In addition to the initial output buffer b, bformata takes the same + parameters as printf (), but rather than outputting results to stdio, it + appends the results to the initial bstring parameter. Note that if + there is an early generation of a '\0' character, the bstring will be + truncated to this end point. + + Note that %s format tokens correspond to '\0' terminated char * buffers, + not bstrings. To print a bstring, first dereference data element of the + the bstring: + + /* b1->data needs to be '\0' terminated, so tagbstrings generated + by blk2tbstr () might not be suitable. */ + bformata (b0 = bfromcstr ("Hello"), ", %s", b1->data); + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bformata function is not present. + + .......................................................................... + + extern int bassignformat (bstring b, const char * fmt, ...); + + After the first parameter, it takes the same parameters as printf (), but + rather than outputting results to stdio, it outputs the results to + the bstring parameter b. Note that if there is an early generation of a + '\0' character, the bstring will be truncated to this end point. + + Note that %s format tokens correspond to '\0' terminated char * buffers, + not bstrings. To print a bstring, first dereference data element of the + the bstring: + + /* b1->data needs to be '\0' terminated, so tagbstrings generated + by blk2tbstr () might not be suitable. */ + bassignformat (b0 = bfromcstr ("Hello"), ", %s", b1->data); + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bassignformat function is not present. + + .......................................................................... + + extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist); + + The bvcformata function formats data under control of the format control + string fmt and attempts to append the result to b. The fmt parameter is + the same as that of the printf function. The variable argument list is + replaced with arglist, which has been initialized by the va_start macro. + The size of the output is upper bounded by count. If the required output + exceeds count, the string b is not augmented with any contents and a value + below BSTR_ERR is returned. If a value below -count is returned then it + is recommended that the negative of this value be used as an update to the + count in a subsequent pass. On other errors, such as running out of + memory, parameter errors or numeric wrap around BSTR_ERR is returned. + BSTR_OK is returned when the output is successfully generated and + appended to b. + + Note: There is no sanity checking of arglist, and this function is + destructive of the contents of b from the b->slen point onward. If there + is an early generation of a '\0' character, the bstring will be truncated + to this end point. + + Although this function is part of the external API for Bstrlib, the + interface and semantics (length limitations, and unusual return codes) + are fairly atypical. The real purpose for this function is to provide an + engine for the bvformata macro. + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bvcformata function is not present. + + .......................................................................... + + extern bstring bread (bNread readPtr, void * parm); + typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, + void *parm); + + Read an entire stream into a bstring, verbatum. The readPtr function + pointer is compatible with fread sematics, except that it need not obtain + the stream data from a file. The intention is that parm would contain + the stream data context/state required (similar to the role of the FILE* + I/O stream parameter of fread.) + + Abstracting the block read function allows for block devices other than + file streams to be read if desired. Note that there is an ANSI + compatibility issue if "fread" is used directly; see the ANSI issues + section below. + + .......................................................................... + + extern int breada (bstring b, bNread readPtr, void * parm); + + Read an entire stream and append it to a bstring, verbatum. Behaves + like bread, except that it appends it results to the bstring b. + BSTR_ERR is returned on error, otherwise 0 is returned. + + .......................................................................... + + extern bstring bgets (bNgetc getcPtr, void * parm, char terminator); + typedef int (* bNgetc) (void * parm); + + Read a bstring from a stream. As many bytes as is necessary are read + until the terminator is consumed or no more characters are available from + the stream. If read from the stream, the terminator character will be + appended to the end of the returned bstring. The getcPtr function must + have the same semantics as the fgetc C library function (i.e., returning + an integer whose value is negative when there are no more characters + available, otherwise the value of the next available unsigned character + from the stream.) The intention is that parm would contain the stream + data context/state required (similar to the role of the FILE* I/O stream + parameter of fgets.) If no characters are read, or there is some other + detectable error, NULL is returned. + + bgets will never call the getcPtr function more often than necessary to + construct its output (including a single call, if required, to determine + that the stream contains no more characters.) + + Abstracting the character stream function and terminator character allows + for different stream devices and string formats other than '\n' + terminated lines in a file if desired (consider \032 terminated email + messages, in a UNIX mailbox for example.) + + For files, this function can be used analogously as fgets as follows: + + fp = fopen ( ... ); + if (fp) b = bgets ((bNgetc) fgetc, fp, '\n'); + + (Note that only one terminator character can be used, and that '\0' is + not assumed to terminate the stream in addition to the terminator + character. This is consistent with the semantics of fgets.) + + .......................................................................... + + extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator); + + Read from a stream and concatenate to a bstring. Behaves like bgets, + except that it appends it results to the bstring b. The value 1 is + returned if no characters are read before a negative result is returned + from getcPtr. Otherwise BSTR_ERR is returned on error, and 0 is returned + in other normal cases. + + .......................................................................... + + extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator); + + Read from a stream and concatenate to a bstring. Behaves like bgets, + except that it assigns the results to the bstring b. The value 1 is + returned if no characters are read before a negative result is returned + from getcPtr. Otherwise BSTR_ERR is returned on error, and 0 is returned + in other normal cases. + + .......................................................................... + + extern struct bStream * bsopen (bNread readPtr, void * parm); + + Wrap a given open stream (described by a fread compatible function + pointer and stream handle) into an open bStream suitable for the bstring + library streaming functions. + + .......................................................................... + + extern void * bsclose (struct bStream * s); + + Close the bStream, and return the handle to the stream that was + originally used to open the given stream. If s is NULL or detectably + invalid, NULL will be returned. + + .......................................................................... + + extern int bsbufflength (struct bStream * s, int sz); + + Set the length of the buffer used by the bStream. If sz is the macro + BSTR_BS_BUFF_LENGTH_GET (which is 0), the length is not set. If s is + NULL or sz is negative, the function will return with BSTR_ERR, otherwise + this function returns with the previous length. + + .......................................................................... + + extern int bsreadln (bstring r, struct bStream * s, char terminator); + + Read a bstring terminated by the terminator character or the end of the + stream from the bStream (s) and return it into the parameter r. The + matched terminator, if found, appears at the end of the line read. If + the stream has been exhausted of all available data, before any can be + read, BSTR_ERR is returned. This function may read additional characters + into the stream buffer from the core stream that are not returned, but + will be retained for subsequent read operations. When reading from high + speed streams, this function can perform significantly faster than bgets. + + .......................................................................... + + extern int bsreadlna (bstring r, struct bStream * s, char terminator); + + Read a bstring terminated by the terminator character or the end of the + stream from the bStream (s) and concatenate it to the parameter r. The + matched terminator, if found, appears at the end of the line read. If + the stream has been exhausted of all available data, before any can be + read, BSTR_ERR is returned. This function may read additional characters + into the stream buffer from the core stream that are not returned, but + will be retained for subsequent read operations. When reading from high + speed streams, this function can perform significantly faster than bgets. + + .......................................................................... + + extern int bsreadlns (bstring r, struct bStream * s, bstring terminators); + + Read a bstring terminated by any character in the terminators bstring or + the end of the stream from the bStream (s) and return it into the + parameter r. This function may read additional characters from the core + stream that are not returned, but will be retained for subsequent read + operations. + + .......................................................................... + + extern int bsreadlnsa (bstring r, struct bStream * s, bstring terminators); + + Read a bstring terminated by any character in the terminators bstring or + the end of the stream from the bStream (s) and concatenate it to the + parameter r. If the stream has been exhausted of all available data, + before any can be read, BSTR_ERR is returned. This function may read + additional characters from the core stream that are not returned, but + will be retained for subsequent read operations. + + .......................................................................... + + extern int bsread (bstring r, struct bStream * s, int n); + + Read a bstring of length n (or, if it is fewer, as many bytes as is + remaining) from the bStream. This function will read the minimum + required number of additional characters from the core stream. When the + stream is at the end of the file BSTR_ERR is returned, otherwise BSTR_OK + is returned. + + .......................................................................... + + extern int bsreada (bstring r, struct bStream * s, int n); + + Read a bstring of length n (or, if it is fewer, as many bytes as is + remaining) from the bStream and concatenate it to the parameter r. This + function will read the minimum required number of additional characters + from the core stream. When the stream is at the end of the file BSTR_ERR + is returned, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int bsunread (struct bStream * s, const_bstring b); + + Insert a bstring into the bStream at the current position. These + characters will be read prior to those that actually come from the core + stream. + + .......................................................................... + + extern int bspeek (bstring r, const struct bStream * s); + + Return the number of currently buffered characters from the bStream that + will be read prior to reads from the core stream, and append it to the + the parameter r. + + .......................................................................... + + extern int bssplitscb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); + + Iterate the set of disjoint sequential substrings over the stream s + divided by any character from the bstring splitStr. The parm passed to + bssplitscb is passed on to cb. If the function cb returns a value < 0, + then further iterating is halted and this return value is returned by + bssplitscb. + + Note: At the point of calling the cb function, the bStream pointer is + pointed exactly at the position right after having read the split + character. The cb function can act on the stream by causing the bStream + pointer to move, and bssplitscb will continue by starting the next split + at the position of the pointer after the return from cb. + + However, if the cb causes the bStream s to be destroyed then the cb must + return with a negative value, otherwise bssplitscb will continue in an + undefined manner. + + This function is provided as way to incrementally parse through a file + or other generic stream that in total size may otherwise exceed the + practical or desired memory available. As with the other split callback + based functions this is abortable and does not impose additional memory + allocation. + + .......................................................................... + + extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); + + Iterate the set of disjoint sequential substrings over the stream s + divided by the entire substring splitStr. The parm passed to + bssplitstrcb is passed on to cb. If the function cb returns a + value < 0, then further iterating is halted and this return value is + returned by bssplitstrcb. + + Note: At the point of calling the cb function, the bStream pointer is + pointed exactly at the position right after having read the split + character. The cb function can act on the stream by causing the bStream + pointer to move, and bssplitstrcb will continue by starting the next + split at the position of the pointer after the return from cb. + + However, if the cb causes the bStream s to be destroyed then the cb must + return with a negative value, otherwise bssplitscb will continue in an + undefined manner. + + This function is provided as way to incrementally parse through a file + or other generic stream that in total size may otherwise exceed the + practical or desired memory available. As with the other split callback + based functions this is abortable and does not impose additional memory + allocation. + + .......................................................................... + + extern int bseof (const struct bStream * s); + + Return the defacto "EOF" (end of file) state of a stream (1 if the + bStream is in an EOF state, 0 if not, and BSTR_ERR if stream is closed or + detectably erroneous.) When the readPtr callback returns a value <= 0 + the stream reaches its "EOF" state. Note that bunread with non-empty + content will essentially turn off this state, and the stream will not be + in its "EOF" state so long as its possible to read more data out of it. + + Also note that the semantics of bseof() are slightly different from + something like feof(). I.e., reaching the end of the stream does not + necessarily guarantee that bseof() will return with a value indicating + that this has happened. bseof() will only return indicating that it has + reached the "EOF" and an attempt has been made to read past the end of + the bStream. + +The macros +---------- + + The macros described below are shown in a prototype form indicating their + intended usage. Note that the parameters passed to these macros will be + referenced multiple times. As with all macros, programmer care is + required to guard against unintended side effects. + + int blengthe (const_bstring b, int err); + + Returns the length of the bstring. If the bstring is NULL err is + returned. + + .......................................................................... + + int blength (const_bstring b); + + Returns the length of the bstring. If the bstring is NULL, the length + returned is 0. + + .......................................................................... + + int bchare (const_bstring b, int p, int c); + + Returns the p'th character of the bstring b. If the position p refers to + a position that does not exist in the bstring or the bstring is NULL, + then c is returned. + + .......................................................................... + + char bchar (const_bstring b, int p); + + Returns the p'th character of the bstring b. If the position p refers to + a position that does not exist in the bstring or the bstring is NULL, + then '\0' is returned. + + .......................................................................... + + char * bdatae (bstring b, char * err); + + Returns the char * data portion of the bstring b. If b is NULL, err is + returned. + + .......................................................................... + + char * bdata (bstring b); + + Returns the char * data portion of the bstring b. If b is NULL, NULL is + returned. + + .......................................................................... + + char * bdataofse (bstring b, int ofs, char * err); + + Returns the char * data portion of the bstring b offset by ofs. If b is + NULL, err is returned. + + .......................................................................... + + char * bdataofs (bstring b, int ofs); + + Returns the char * data portion of the bstring b offset by ofs. If b is + NULL, NULL is returned. + + .......................................................................... + + struct tagbstring var = bsStatic ("..."); + + The bsStatic macro allows for static declarations of literal string + constants as struct tagbstring structures. The resulting tagbstring does + not need to be freed or destroyed. Note that this macro is only well + defined for string literal arguments. For more general string pointers, + use the btfromcstr macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + <- bsStaticBlkParms ("...") + + The bsStaticBlkParms macro emits a pair of comma seperated parameters + corresponding to the block parameters for the block functions in Bstrlib + (i.e., blk2bstr, bcatblk, blk2tbstr, bisstemeqblk, bisstemeqcaselessblk.) + Note that this macro is only well defined for string literal arguments. + + Examples: + + bstring b = blk2bstr (bsStaticBlkParms ("Fast init. ")); + bcatblk (b, bsStaticBlkParms ("No frills fast concatenation.")); + + These are faster than using bfromcstr() and bcatcstr() respectively + because the length of the inline string is known as a compile time + constant. Also note that seperate struct tagbstring declarations for + holding the output of a bsStatic() macro are not required. + + .......................................................................... + + void btfromcstr (struct tagbstring& t, const char * s); + + Fill in the tagbstring t with the '\0' terminated char buffer s. This + action is purely reference oriented; no memory management is done. The + data member is just assigned s, and slen is assigned the strlen of s. + The s parameter is accessed exactly once in this macro. + + The resulting struct tagbstring is initially write protected. Attempts + to write to this struct tagbstring in a write protected state from any + bstrlib function will lead to BSTR_ERR being returned. Invoke the + bwriteallow on this struct tagbstring to make it writeable (though this + requires that s be obtained from a function compatible with malloc.) + + .......................................................................... + + void btfromblk (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len. This + action is purely reference oriented; no memory management is done. The + data member of t is just assigned s, and slen is assigned len. Note that + the buffer is not appended with a '\0' character. The s and len + parameters are accessed exactly once each in this macro. + + The resulting struct tagbstring is initially write protected. Attempts + to write to this struct tagbstring in a write protected state from any + bstrlib function will lead to BSTR_ERR being returned. Invoke the + bwriteallow on this struct tagbstring to make it writeable (though this + requires that s be obtained from a function compatible with malloc.) + + .......................................................................... + + void btfromblkltrimws (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len after it + has been left trimmed. This action is purely reference oriented; no + memory management is done. The data member of t is just assigned to a + pointer inside the buffer s. Note that the buffer is not appended with a + '\0' character. The s and len parameters are accessed exactly once each + in this macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + void btfromblkrtrimws (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len after it + has been right trimmed. This action is purely reference oriented; no + memory management is done. The data member of t is just assigned to a + pointer inside the buffer s. Note that the buffer is not appended with a + '\0' character. The s and len parameters are accessed exactly once each + in this macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + void btfromblktrimws (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len after it + has been left and right trimmed. This action is purely reference + oriented; no memory management is done. The data member of t is just + assigned to a pointer inside the buffer s. Note that the buffer is not + appended with a '\0' character. The s and len parameters are accessed + exactly once each in this macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + void bmid2tbstr (struct tagbstring& t, bstring b, int pos, int len); + + Fill the tagbstring t with the substring from b, starting from position + pos with a length len. The segment is clamped by the boundaries of + the bstring b. This action is purely reference oriented; no memory + management is done. Note that the buffer is not appended with a '\0' + character. Note that the t parameter to this macro may be accessed + multiple times. Note that the contents of t will become undefined + if the contents of b change or are destroyed. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring in a write protected state from any + bstrlib function will lead to BSTR_ERR being returned. Invoking the + bwriteallow macro on this struct tagbstring will have no effect. + + .......................................................................... + + void bvformata (int& ret, bstring b, const char * format, lastarg); + + Append the bstring b with printf like formatting with the format control + string, and the arguments taken from the ... list of arguments after + lastarg passed to the containing function. If the containing function + does not have ... parameters or lastarg is not the last named parameter + before the ... then the results are undefined. If successful, the + results are appended to b and BSTR_OK is assigned to ret. Otherwise + BSTR_ERR is assigned to ret. + + Example: + + void dbgerror (FILE * fp, const char * fmt, ...) { + int ret; + bstring b; + bvformata (ret, b = bfromcstr ("DBG: "), fmt, fmt); + if (BSTR_OK == ret) fputs ((char *) bdata (b), fp); + bdestroy (b); + } + + Note that if the BSTRLIB_NOVSNP macro was set when bstrlib had been + compiled the bvformata macro will not link properly. If the + BSTRLIB_NOVSNP macro has been set, the bvformata macro will not be + available. + + .......................................................................... + + void bwriteprotect (struct tagbstring& t); + + Disallow bstring from being written to via the bstrlib API. Attempts to + write to the resulting tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. + + Note: bstrings which are write protected cannot be destroyed via bdestroy. + + Note to C++ users: Setting a CBString as write protected will not prevent + it from being destroyed by the destructor. + + .......................................................................... + + void bwriteallow (struct tagbstring& t); + + Allow bstring to be written to via the bstrlib API. Note that such an + action makes the bstring both writable and destroyable. If the bstring is + not legitimately writable (as is the case for struct tagbstrings + initialized with a bsStatic value), the results of this are undefined. + + Note that invoking the bwriteallow macro may increase the number of + reallocs by one more than necessary for every call to bwriteallow + interleaved with any bstring API which writes to this bstring. + + .......................................................................... + + int biswriteprotected (struct tagbstring& t); + + Returns 1 if the bstring is write protected, otherwise 0 is returned. + +=============================================================================== + +The bstest module +----------------- + +The bstest module is just a unit test for the bstrlib module. For correct +implementations of bstrlib, it should execute with 0 failures being reported. +This test should be utilized if modifications/customizations to bstrlib have +been performed. It tests each core bstrlib function with bstrings of every +mode (read-only, NULL, static and mutable) and ensures that the expected +semantics are observed (including results that should indicate an error). It +also tests for aliasing support. Passing bstest is a necessary but not a +sufficient condition for ensuring the correctness of the bstrlib module. + + +The test module +--------------- + +The test module is just a unit test for the bstrwrap module. For correct +implementations of bstrwrap, it should execute with 0 failures being +reported. This test should be utilized if modifications/customizations to +bstrwrap have been performed. It tests each core bstrwrap function with +CBStrings write protected or not and ensures that the expected semantics are +observed (including expected exceptions.) Note that exceptions cannot be +disabled to run this test. Passing test is a necessary but not a sufficient +condition for ensuring the correctness of the bstrwrap module. + +=============================================================================== + +Using Bstring and CBString as an alternative to the C library +------------------------------------------------------------- + +First let us give a table of C library functions and the alternative bstring +functions and CBString methods that should be used instead of them. + +C-library Bstring alternative CBString alternative +--------- ------------------- -------------------- +gets bgets ::gets +strcpy bassign = operator +strncpy bassignmidstr ::midstr +strcat bconcat += operator +strncat bconcat + btrunc += operator + ::trunc +strtok bsplit, bsplits ::split +sprintf b(assign)format ::format +snprintf b(assign)format + btrunc ::format + ::trunc +vsprintf bvformata bvformata + +vsnprintf bvformata + btrunc bvformata + btrunc +vfprintf bvformata + fputs use bvformata + fputs +strcmp biseq, bstrcmp comparison operators. +strncmp bstrncmp, memcmp bstrncmp, memcmp +strlen ->slen, blength ::length +strdup bstrcpy constructor +strset bpattern ::fill +strstr binstr ::find +strpbrk binchr ::findchr +stricmp bstricmp cast & use bstricmp +strlwr btolower cast & use btolower +strupr btoupper cast & use btoupper +strrev bReverse (aux module) cast & use bReverse +strchr bstrchr cast & use bstrchr +strspnp use strspn use strspn +ungetc bsunread bsunread + +The top 9 C functions listed here are troublesome in that they impose memory +management in the calling function. The Bstring and CBstring interfaces have +built-in memory management, so there is far less code with far less potential +for buffer overrun problems. strtok can only be reliably called as a "leaf" +calculation, since it (quite bizarrely) maintains hidden internal state. And +gets is well known to be broken no matter what. The Bstrlib alternatives do +not suffer from those sorts of problems. + +The substitute for strncat can be performed with higher performance by using +the blk2tbstr macro to create a presized second operand for bconcat. + +C-library Bstring alternative CBString alternative +--------- ------------------- -------------------- +strspn strspn acceptable strspn acceptable +strcspn strcspn acceptable strcspn acceptable +strnset strnset acceptable strnset acceptable +printf printf acceptable printf acceptable +puts puts acceptable puts acceptable +fprintf fprintf acceptable fprintf acceptable +fputs fputs acceptable fputs acceptable +memcmp memcmp acceptable memcmp acceptable + +Remember that Bstring (and CBstring) functions will automatically append the +'\0' character to the character data buffer. So by simply accessing the data +buffer directly, ordinary C string library functions can be called directly +on them. Note that bstrcmp is not the same as memcmp in exactly the same way +that strcmp is not the same as memcmp. + +C-library Bstring alternative CBString alternative +--------- ------------------- -------------------- +fread balloc + fread ::alloc + fread +fgets balloc + fgets ::alloc + fgets + +These are odd ones because of the exact sizing of the buffer required. The +Bstring and CBString alternatives requires that the buffers are forced to +hold at least the prescribed length, then just use fread or fgets directly. +However, typically the automatic memory management of Bstring and CBstring +will make the typical use of fgets and fread to read specifically sized +strings unnecessary. + +Implementation Choices +---------------------- + +Overhead: +......... + +The bstring library has more overhead versus straight char buffers for most +functions. This overhead is essentially just the memory management and +string header allocation. This overhead usually only shows up for small +string manipulations. The performance loss has to be considered in +light of the following: + +1) What would be the performance loss of trying to write this management + code in one's own application? +2) Since the bstring library source code is given, a sufficiently powerful + modern inlining globally optimizing compiler can remove function call + overhead. + +Since the data type is exposed, a developer can replace any unsatisfactory +function with their own inline implementation. And that is besides the main +point of what the better string library is mainly meant to provide. Any +overhead lost has to be compared against the value of the safe abstraction +for coupling memory management and string functionality. + +Performance of the C interface: +............................... + +The algorithms used have performance advantages versus the analogous C +library functions. For example: + +1. bfromcstr/blk2str/bstrcpy versus strcpy/strdup. By using memmove instead + of strcpy, the break condition of the copy loop is based on an independent + counter (that should be allocated in a register) rather than having to + check the results of the load. Modern out-of-order executing CPUs can + parallelize the final branch mis-predict penality with the loading of the + source string. Some CPUs will also tend to have better built-in hardware + support for counted memory moves than load-compare-store. (This is a + minor, but non-zero gain.) +2. biseq versus strcmp. If the strings are unequal in length, bsiseq will + return in O(1) time. If the strings are aliased, or have aliased data + buffers, biseq will return in O(1) time. strcmp will always be O(k), + where k is the length of the common prefix or the whole string if they are + identical. +3. ->slen versus strlen. ->slen is obviously always O(1), while strlen is + always O(n) where n is the length of the string. +4. bconcat versus strcat. Both rely on precomputing the length of the + destination string argument, which will favor the bstring library. On + iterated concatenations the performance difference can be enormous. +5. bsreadln versus fgets. The bsreadln function reads large blocks at a time + from the given stream, then parses out lines from the buffers directly. + Some C libraries will implement fgets as a loop over single fgetc calls. + Testing indicates that the bsreadln approach can be several times faster + for fast stream devices (such as a file that has been entirely cached.) +6. bsplits/bsplitscb versus strspn. Accelerators for the set of match + characters are generated only once. +7. binstr versus strstr. The binstr implementation unrolls the loops to + help reduce loop overhead. This will matter if the target string is + long and source string is not found very early in the target string. + With strstr, while it is possible to unroll the source contents, it is + not possible to do so with the destination contents in a way that is + effective because every destination character must be tested against + '\0' before proceeding to the next character. +8. bReverse versus strrev. The C function must find the end of the string + first before swaping character pairs. +9. bstrrchr versus no comparable C function. Its not hard to write some C + code to search for a character from the end going backwards. But there + is no way to do this without computing the length of the string with + strlen. + +Practical testing indicates that in general Bstrlib is never signifcantly +slower than the C library for common operations, while very often having a +performance advantage that ranges from significant to massive. Even for +functions like b(n)inchr versus str(c)spn() (where, in theory, there is no +advantage for the Bstrlib architecture) the performance of Bstrlib is vastly +superior to most tested C library implementations. + +Some of Bstrlib's extra functionality also lead to inevitable performance +advantages over typical C solutions. For example, using the blk2tbstr macro, +one can (in O(1) time) generate an internal substring by reference while not +disturbing the original string. If disturbing the original string is not an +option, typically, a comparable char * solution would have to make a copy of +the substring to provide similar functionality. Another example is reverse +character set scanning -- the str(c)spn functions only scan in a forward +direction which can complicate some parsing algorithms. + +Where high performance char * based algorithms are available, Bstrlib can +still leverage them by accessing the ->data field on bstrings. So +realistically Bstrlib can never be significantly slower than any standard +'\0' terminated char * based solutions. + +Performance of the C++ interface: +................................. + +The C++ interface has been designed with an emphasis on abstraction and safety +first. However, since it is substantially a wrapper for the C bstring +functions, for longer strings the performance comments described in the +"Performance of the C interface" section above still apply. Note that the +(CBString *) type can be directly cast to a (bstring) type, and passed as +parameters to the C functions (though a CBString must never be passed to +bdestroy.) + +Probably the most controversial choice is performing full bounds checking on +the [] operator. This decision was made because 1) the fast alternative of +not bounds checking is still available by first casting the CBString to a +(const char *) buffer or to a (struct tagbstring) then derefencing .data and +2) because the lack of bounds checking is seen as one of the main weaknesses +of C/C++ versus other languages. This check being done on every access leads +to individual character extraction being actually slower than other languages +in this one respect (other language's compilers will normally dedicate more +resources on hoisting or removing bounds checking as necessary) but otherwise +bring C++ up to the level of other languages in terms of functionality. + +It is common for other C++ libraries to leverage the abstractions provided by +C++ to use reference counting and "copy on write" policies. While these +techniques can speed up some scenarios, they impose a problem with respect to +thread safety. bstrings and CBStrings can be properly protected with +"per-object" mutexes, meaning that two bstrlib calls can be made and execute +simultaneously, so long as the bstrings and CBstrings are distinct. With a +reference count and alias before copy on write policy, global mutexes are +required that prevent multiple calls to the strings library to execute +simultaneously regardless of whether or not the strings represent the same +string. + +One interesting trade off in CBString is that the default constructor is not +trivial. I.e., it always prepares a ready to use memory buffer. The purpose +is to ensure that there is a uniform internal composition for any functioning +CBString that is compatible with bstrings. It also means that the other +methods in the class are not forced to perform "late initialization" checks. +In the end it means that construction of CBStrings are slower than other +comparable C++ string classes. Initial testing, however, indicates that +CBString outperforms std::string and MFC's CString, for example, in all other +operations. So to work around this weakness it is recommended that CBString +declarations be pushed outside of inner loops. + +Practical testing indicates that with the exception of the caveats given +above (constructors and safe index character manipulations) the C++ API for +Bstrlib generally outperforms popular standard C++ string classes. Amongst +the standard libraries and compilers, the quality of concatenation operations +varies wildly and very little care has gone into search functions. Bstrlib +dominates those performance benchmarks. + +Memory management: +.................. + +The bstring functions which write and modify bstrings will automatically +reallocate the backing memory for the char buffer whenever it is required to +grow. The algorithm for resizing chosen is to snap up to sizes that are a +power of two which are sufficient to hold the intended new size. Memory +reallocation is not performed when the required size of the buffer is +decreased. This behavior can be relied on, and is necessary to make the +behaviour of balloc deterministic. This trades off additional memory usage +for decreasing the frequency for required reallocations: + +1. For any bstring whose size never exceeds n, its buffer is not ever + reallocated more than log_2(n) times for its lifetime. +2. For any bstring whose size never exceeds n, its buffer is never more than + 2*(n+1) in length. (The extra characters beyond 2*n are to allow for the + implicit '\0' which is always added by the bstring modifying functions.) + +Decreasing the buffer size when the string decreases in size would violate 1) +above and in real world case lead to pathological heap thrashing. Similarly, +allocating more tightly than "least power of 2 greater than necessary" would +lead to a violation of 1) and have the same potential for heap thrashing. + +Property 2) needs emphasizing. Although the memory allocated is always a +power of 2, for a bstring that grows linearly in size, its buffer memory also +grows linearly, not exponentially. The reason is that the amount of extra +space increases with each reallocation, which decreases the frequency of +future reallocations. + +Obviously, given that bstring writing functions may reallocate the data +buffer backing the target bstring, one should not attempt to cache the data +buffer address and use it after such bstring functions have been called. +This includes making reference struct tagbstrings which alias to a writable +bstring. + +balloc or bfromcstralloc can be used to preallocate the minimum amount of +space used for a given bstring. This will reduce even further the number of +times the data portion is reallocated. If the length of the string is never +more than one less than the memory length then there will be no further +reallocations. + +Note that invoking the bwriteallow macro may increase the number of reallocs +by one more than necessary for every call to bwriteallow interleaved with any +bstring API which writes to this bstring. + +The library does not use any mechanism for automatic clean up for the C API. +Thus explicit clean up via calls to bdestroy() are required to avoid memory +leaks. + +Constant and static tagbstrings: +................................ + +A struct tagbstring can be write protected from any bstrlib function using +the bwriteprotect macro. A write protected struct tagbstring can then be +reset to being writable via the bwriteallow macro. There is, of course, no +protection from attempts to directly access the bstring members. Modifying a +bstring which is write protected by direct access has undefined behavior. + +static struct tagbstrings can be declared via the bsStatic macro. They are +considered permanently unwritable. Such struct tagbstrings's are declared +such that attempts to write to it are not well defined. Invoking either +bwriteallow or bwriteprotect on static struct tagbstrings has no effect. + +struct tagbstring's initialized via btfromcstr or blk2tbstr are protected by +default but can be made writeable via the bwriteallow macro. If bwriteallow +is called on such struct tagbstring's, it is the programmer's responsibility +to ensure that: + +1) the buffer supplied was allocated from the heap. +2) bdestroy is not called on this tagbstring (unless the header itself has + also been allocated from the heap.) +3) free is called on the buffer to reclaim its memory. + +bwriteallow and bwriteprotect can be invoked on ordinary bstrings (they have +to be dereferenced with the (*) operator to get the levels of indirection +correct) to give them write protection. + +Buffer declaration: +................... + +The memory buffer is actually declared "unsigned char *" instead of "char *". +The reason for this is to trigger compiler warnings whenever uncasted char +buffers are assigned to the data portion of a bstring. This will draw more +diligent programmers into taking a second look at the code where they +have carelessly left off the typically required cast. (Research from +AT&T/Lucent indicates that additional programmer eyeballs is one of the most +effective mechanisms at ferreting out bugs.) + +Function pointers: +.................. + +The bgets, bread and bStream functions use function pointers to obtain +strings from data streams. The function pointer declarations have been +specifically chosen to be compatible with the fgetc and fread functions. +While this may seem to be a convoluted way of implementing fgets and fread +style functionality, it has been specifically designed this way to ensure +that there is no dependency on a single narrowly defined set of device +interfaces, such as just stream I/O. In the embedded world, its quite +possible to have environments where such interfaces may not exist in the +standard C library form. Furthermore, the generalization that this opens up +allows for more sophisticated uses for these functions (performing an fgets +like function on a socket, for example.) By using function pointers, it also +allows such abstract stream interfaces to be created using the bstring library +itself while not creating a circular dependency. + +Use of int's for sizes: +....................... + +This is just a recognition that 16bit platforms with requirements for strings +that are larger than 64K and 32bit+ platforms with requirements for strings +that are larger than 4GB are pretty marginal. The main focus is for 32bit +platforms, and emerging 64bit platforms with reasonable < 4GB string +requirements. Using ints allows for negative values which has meaning +internally to bstrlib. + +Semantic consideration: +....................... + +Certain care needs to be taken when copying and aliasing bstrings. A bstring +is essentially a pointer type which points to a multipart abstract data +structure. Thus usage, and lifetime of bstrings have semantics that follow +these considerations. For example: + + bstring a, b; + struct tagbstring t; + + a = bfromcstr("Hello"); /* Create new bstring and copy "Hello" into it. */ + b = a; /* Alias b to the contents of a. */ + t = *a; /* Create a current instance pseudo-alias of a. */ + bconcat (a, b); /* Double a and b, t is now undefined. */ + bdestroy (a); /* Destroy the contents of both a and b. */ + +Variables of type bstring are really just references that point to real +bstring objects. The equal operator (=) creates aliases, and the asterisk +dereference operator (*) creates a kind of alias to the current instance (which +is generally not useful for any purpose.) Using bstrcpy() is the correct way +of creating duplicate instances. The ampersand operator (&) is useful for +creating aliases to struct tagbstrings (remembering that constructed struct +tagbstrings are not writable by default.) + +CBStrings use complete copy semantics for the equal operator (=), and thus do +not have these sorts of issues. + +Debugging: +.......... + +Bstrings have a simple, exposed definition and construction, and the library +itself is open source. So most debugging is going to be fairly straight- +forward. But the memory for bstrings come from the heap, which can often be +corrupted indirectly, and it might not be obvious what has happened even from +direct examination of the contents in a debugger or a core dump. There are +some tools such as Purify, Insure++ and Electric Fence which can help solve +such problems, however another common approach is to directly instrument the +calls to malloc, realloc, calloc, free, memcpy, memmove and/or other calls +by overriding them with macro definitions. + +Although the user could hack on the Bstrlib sources directly as necessary to +perform such an instrumentation, Bstrlib comes with a built-in mechanism for +doing this. By defining the macro BSTRLIB_MEMORY_DEBUG and providing an +include file named memdbg.h this will force the core Bstrlib modules to +attempt to include this file. In such a file, macros could be defined which +overrides Bstrlib's useage of the C standard library. + +Rather than calling malloc, realloc, free, memcpy or memmove directly, Bstrlib +emits the macros bstr__alloc, bstr__realloc, bstr__free, bstr__memcpy and +bstr__memmove in their place respectively. By default these macros are simply +assigned to be equivalent to their corresponding C standard library function +call. However, if they are given earlier macro definitions (via the back +door include file) they will not be given their default definition. In this +way Bstrlib's interface to the standard library can be changed but without +having to directly redefine or link standard library symbols (both of which +are not strictly ANSI C compliant.) + +An example definition might include: + + #define bstr__alloc(sz) X_malloc ((sz), __LINE__, __FILE__) + +which might help contextualize heap entries in a debugging environment. + +The NULL parameter and sanity checking of bstrings is part of the Bstrlib +API, and thus Bstrlib itself does not present any different modes which would +correspond to "Debug" or "Release" modes. Bstrlib always contains mechanisms +which one might think of as debugging features, but retains the performance +and small memory footprint one would normally associate with release mode +code. + +Integration Microsoft's Visual Studio debugger: +............................................... + +Microsoft's Visual Studio debugger has a capability of customizable mouse +float over data type descriptions. This is accomplished by editting the +AUTOEXP.DAT file to include the following: + + ; new for CBString + tagbstring =slen= mlen= + Bstrlib::CBStringList =count= + +In Visual C++ 6.0 this file is located in the directory: + + C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin + +and in Visual Studio .NET 2003 its located here: + + C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\Debugger + +This will improve the ability of debugging with Bstrlib under Visual Studio. + +Security +-------- + +Bstrlib does not come with explicit security features outside of its fairly +comprehensive error detection, coupled with its strict semantic support. +That is to say that certain common security problems, such as buffer overrun, +constant overwrite, arbitrary truncation etc, are far less likely to happen +inadvertently. Where it does help, Bstrlib maximizes its advantage by +providing developers a simple adoption path that lets them leave less secure +string mechanisms behind. The library will not leave developers wanting, so +they will be less likely to add new code using a less secure string library +to add functionality that might be missing from Bstrlib. + +That said there are a number of security ideas not addressed by Bstrlib: + +1. Race condition exploitation (i.e., verifying a string's contents, then +raising the privilege level and execute it as a shell command as two +non-atomic steps) is well beyond the scope of what Bstrlib can provide. It +should be noted that MFC's built-in string mutex actually does not solve this +problem either -- it just removes immediate data corruption as a possible +outcome of such exploit attempts (it can be argued that this is worse, since +it will leave no trace of the exploitation). In general race conditions have +to be dealt with by careful design and implementation; it cannot be assisted +by a string library. + +2. Any kind of access control or security attributes to prevent usage in +dangerous interfaces such as system(). Perl includes a "trust" attribute +which can be endowed upon strings that are intended to be passed to such +dangerous interfaces. However, Perl's solution reflects its own limitations +-- notably that it is not a strongly typed language. In the example code for +Bstrlib, there is a module called taint.cpp. It demonstrates how to write a +simple wrapper class for managing "untainted" or trusted strings using the +type system to prevent questionable mixing of ordinary untrusted strings with +untainted ones then passing them to dangerous interfaces. In this way the +security correctness of the code reduces to auditing the direct usages of +dangerous interfaces or promotions of tainted strings to untainted ones. + +3. Encryption of string contents is way beyond the scope of Bstrlib. +Maintaining encrypted string contents in the futile hopes of thwarting things +like using system-level debuggers to examine sensitive string data is likely +to be a wasted effort (imagine a debugger that runs at a higher level than a +virtual processor where the application runs). For more standard encryption +usages, since the bstring contents are simply binary blocks of data, this +should pose no problem for usage with other standard encryption libraries. + +Compatibility +------------- + +The Better String Library is known to compile and function correctly with the +following compilers: + + - Microsoft Visual C++ + - Watcom C/C++ + - Intel's C/C++ compiler (Windows) + - The GNU C/C++ compiler (cygwin and Linux on PPC64) + - Borland C + - Turbo C + +Setting of configuration options should be unnecessary for these compilers +(unless exceptions are being disabled or STLport has been added to WATCOM +C/C++). Bstrlib has been developed with an emphasis on portability. As such +porting it to other compilers should be straight forward. This package +includes a porting guide (called porting.txt) which explains what issues may +exist for porting Bstrlib to different compilers and environments. + +ANSI issues +----------- + +1. The function pointer types bNgetc and bNread have prototypes which are very +similar to, but not exactly the same as fgetc and fread respectively. +Basically the FILE * parameter is replaced by void *. The purpose of this +was to allow one to create other functions with fgetc and fread like +semantics without being tied to ANSI C's file streaming mechanism. I.e., one +could very easily adapt it to sockets, or simply reading a block of memory, +or procedurally generated strings (for fractal generation, for example.) + +The problem is that invoking the functions (bNgetc)fgetc and (bNread)fread is +not technically legal in ANSI C. The reason being that the compiler is only +able to coerce the function pointers themselves into the target type, however +are unable to perform any cast (implicit or otherwise) on the parameters +passed once invoked. I.e., if internally void * and FILE * need some kind of +mechanical coercion, the compiler will not properly perform this conversion +and thus lead to undefined behavior. + +Apparently a platform from Data General called "Eclipse" and another from +Tandem called "NonStop" have a different representation for pointers to bytes +and pointers to words, for example, where coercion via casting is necessary. +(Actual confirmation of the existence of such machines is hard to come by, so +it is prudent to be skeptical about this information.) However, this is not +an issue for any known contemporary platforms. One may conclude that such +platforms are effectively apocryphal even if they do exist. + +To correctly work around this problem to the satisfaction of the ANSI +limitations, one needs to create wrapper functions for fgets and/or +fread with the prototypes of bNgetc and/or bNread respectively which performs +no other action other than to explicitely cast the void * parameter to a +FILE *, and simply pass the remaining parameters straight to the function +pointer call. + +The wrappers themselves are trivial: + + size_t freadWrap (void * buff, size_t esz, size_t eqty, void * parm) { + return fread (buff, esz, eqty, (FILE *) parm); + } + + int fgetcWrap (void * parm) { + return fgetc ((FILE *) parm); + } + +These have not been supplied in bstrlib or bstraux to prevent unnecessary +linking with file I/O functions. + +2. vsnprintf is not available on all compilers. Because of this, the bformat +and bformata functions (and format and formata methods) are not guaranteed to +work properly. For those compilers that don't have vsnprintf, the +BSTRLIB_NOVSNP macro should be set before compiling bstrlib, and the format +functions/method will be disabled. + +The more recent ANSI C standards have specified the required inclusion of a +vsnprintf function. + +3. The bstrlib function names are not unique in the first 6 characters. This +is only an issue for older C compiler environments which do not store more +than 6 characters for function names. + +4. The bsafe module defines macros and function names which are part of the +C library. This simply overrides the definition as expected on all platforms +tested, however it is not sanctioned by the ANSI standard. This module is +clearly optional and should be omitted on platforms which disallow its +undefined semantics. + +In practice the real issue is that some compilers in some modes of operation +can/will inline these standard library functions on a module by module basis +as they appear in each. The linker will thus have no opportunity to override +the implementation of these functions for those cases. This can lead to +inconsistent behaviour of the bsafe module on different platforms and +compilers. + +=============================================================================== + +Comparison with Microsoft's CString class +----------------------------------------- + +Although developed independently, CBStrings have very similar functionality to +Microsoft's CString class. However, the bstring library has significant +advantages over CString: + +1. Bstrlib is a C-library as well as a C++ library (using the C++ wrapper). + + - Thus it is compatible with more programming environments and + available to a wider population of programmers. + +2. The internal structure of a bstring is considered exposed. + + - A single contiguous block of data can be cut into read-only pieces by + simply creating headers, without allocating additional memory to create + reference copies of each of these sub-strings. + - In this way, using bstrings in a totally abstracted way becomes a choice + rather than an imposition. Further this choice can be made differently + at different layers of applications that use it. + +3. Static declaration support precludes the need for constructor + invocation. + + - Allows for static declarations of constant strings that has no + additional constructor overhead. + +4. Bstrlib is not attached to another library. + + - Bstrlib is designed to be easily plugged into any other library + collection, without dependencies on other libraries or paradigms (such + as "MFC".) + +The bstring library also comes with a few additional functions that are not +available in the CString class: + + - bsetstr + - bsplit + - bread + - breplace (this is different from CString::Replace()) + - Writable indexed characters (for example a[i]='x') + +Interestingly, although Microsoft did implement mid$(), left$() and right$() +functional analogues (these are functions from GWBASIC) they seem to have +forgotten that mid$() could be also used to write into the middle of a string. +This functionality exists in Bstrlib with the bsetstr() and breplace() +functions. + +Among the disadvantages of Bstrlib is that there is no special support for +localization or wide characters. Such things are considered beyond the scope +of what bstrings are trying to deliver. CString essentially supports the +older UCS-2 version of Unicode via widechar_t as an application-wide compile +time switch. + +CString's also use built-in mechanisms for ensuring thread safety under all +situations. While this makes writing thread safe code that much easier, this +built-in safety feature has a price -- the inner loops of each CString method +runs in its own critical section (grabbing and releasing a light weight mutex +on every operation.) The usual way to decrease the impact of a critical +section performance penalty is to amortize more operations per critical +section. But since the implementation of CStrings is fixed as a one critical +section per-operation cost, there is no way to leverage this common +performance enhancing idea. + +The search facilities in Bstrlib are comparable to those in MFC's CString +class, though it is missing locale specific collation. But because Bstrlib +is interoperable with C's char buffers, it will allow programmers to write +their own string searching mechanism (such as Boyer-Moore), or be able to +choose from a variety of available existing string searching libraries (such +as those for regular expressions) without difficulty. + +Microsoft used a very non-ANSI conforming trick in its implementation to +allow printf() to use the "%s" specifier to output a CString correctly. This +can be convenient, but it is inherently not portable. CBString requires an +explicit cast, while bstring requires the data member to be dereferenced. +Microsoft's own documentation recommends casting, instead of relying on this +feature. + +Comparison with C++'s std::string +--------------------------------- + +This is the C++ language's standard STL based string class. + +1. There is no C implementation. +2. The [] operator is not bounds checked. +3. Missing a lot of useful functions like printf-like formatting. +4. Some sub-standard std::string implementations (SGI) are necessarily unsafe + to use with multithreading. +5. Limited by STL's std::iostream which in turn is limited by ifstream which + can only take input from files. (Compare to CBStream's API which can take + abstracted input.) +6. Extremely uneven performance across implementations. + +Comparison with ISO C TR 24731 proposal +--------------------------------------- + +Following the ISO C99 standard, Microsoft has proposed a group of C library +extensions which are supposedly "safer and more secure". This proposal is +expected to be adopted by the ISO C standard which follows C99. + +The proposal reveals itself to be very similar to Microsoft's "StrSafe" +library. The functions are basically the same as other standard C library +string functions except that destination parameters are paired with an +additional length parameter of type rsize_t. rsize_t is the same as size_t, +however, the range is checked to make sure its between 1 and RSIZE_MAX. Like +Bstrlib, the functions perform a "parameter check". Unlike Bstrlib, when a +parameter check fails, rather than simply outputing accumulatable error +statuses, they call a user settable global error function handler, and upon +return of control performs no (additional) detrimental action. The proposal +covers basic string functions as well as a few non-reenterable functions +(asctime, ctime, and strtok). + +1. Still based solely on char * buffers (and therefore strlen() and strcat() + is still O(n), and there are no faster streq() comparison functions.) +2. No growable string semantics. +3. Requires manual buffer length synchronization in the source code. +4. No attempt to enhance functionality of the C library. +5. Introduces a new error scenario (strings exceeding RSIZE_MAX length). + +The hope is that by exposing the buffer length requirements there will be +fewer buffer overrun errors. However, the error modes are really just +transformed, rather than removed. The real problem of buffer overflows is +that they all happen as a result of erroneous programming. So forcing +programmers to manually deal with buffer limits, will make them more aware of +the problem but doesn't remove the possibility of erroneous programming. So +a programmer that erroneously mixes up the rsize_t parameters is no better off +from a programmer that introduces potential buffer overflows through other +more typical lapses. So at best this may reduce the rate of erroneous +programming, rather than making any attempt at removing failure modes. + +The error handler can discriminate between types of failures, but does not +take into account any callsite context. So the problem is that the error is +going to be manifest in a piece of code, but there is no pointer to that +code. It would seem that passing in the call site __FILE__, __LINE__ as +parameters would be very useful, but the API clearly doesn't support such a +thing (it would increase code bloat even more than the extra length +parameter does, and would require macro tricks to implement). + +The Bstrlib C API takes the position that error handling needs to be done at +the callsite, and just tries to make it as painless as possible. Furthermore, +error modes are removed by supporting auto-growing strings and aliasing. For +capturing errors in more central code fragments, Bstrlib's C++ API uses +exception handling extensively, which is superior to the leaf-only error +handler approach. + +Comparison with Managed String Library CERT proposal +---------------------------------------------------- + +The main webpage for the managed string library: +http://www.cert.org/secure-coding/managedstring.html + +Robert Seacord at CERT has proposed a C string library that he calls the +"Managed String Library" for C. Like Bstrlib, it introduces a new type +which is called a managed string. The structure of a managed string +(string_m) is like a struct tagbstring but missing the length field. This +internal structure is considered opaque. The length is, like the C standard +library, always computed on the fly by searching for a terminating NUL on +every operation that requires it. So it suffers from every performance +problem that the C standard library suffers from. Interoperating with C +string APIs (like printf, fopen, or anything else that takes a string +parameter) requires copying to additionally allocating buffers that have to +be manually freed -- this makes this library probably slower and more +cumbersome than any other string library in existence. + +The library gives a fully populated error status as the return value of every +string function. The hope is to be able to diagnose all problems +specifically from the return code alone. Comparing this to Bstrlib, which +aways returns one consistent error message, might make it seem that Bstrlib +would be harder to debug; but this is not true. With Bstrlib, if an error +occurs there is always enough information from just knowing there was an error +and examining the parameters to deduce exactly what kind of error has +happened. The managed string library thus gives up nested function calls +while achieving little benefit, while Bstrlib does not. + +One interesting feature that "managed strings" has is the idea of data +sanitization via character set whitelisting. That is to say, a globally +definable filter that makes any attempt to put invalid characters into strings +lead to an error and not modify the string. The author gives the following +example: + + // create valid char set + if (retValue = strcreate_m(&str1, "abc") ) { + fprintf( + stderr, + "Error %d from strcreate_m.\n", + retValue + ); + } + if (retValue = setcharset(str1)) { + fprintf( + stderr, + "Error %d from setcharset().\n", + retValue + ); + } + if (retValue = strcreate_m(&str1, "aabbccabc")) { + fprintf( + stderr, + "Error %d from strcreate_m.\n", + retValue + ); + } + // create string with invalid char set + if (retValue = strcreate_m(&str1, "abbccdabc")) { + fprintf( + stderr, + "Error %d from strcreate_m.\n", + retValue + ); + } + +Which we can compare with a more Bstrlib way of doing things: + + bstring bCreateWithFilter (const char * cstr, const_bstring filter) { + bstring b = bfromcstr (cstr); + if (BSTR_ERR != bninchr (b, filter) && NULL != b) { + fprintf (stderr, "Filter violation.\n"); + bdestroy (b); + b = NULL; + } + return b; + } + + struct tagbstring charFilter = bsStatic ("abc"); + bstring str1 = bCreateWithFilter ("aabbccabc", &charFilter); + bstring str2 = bCreateWithFilter ("aabbccdabc", &charFilter); + +The first thing we should notice is that with the Bstrlib approach you can +have different filters for different strings if necessary. Furthermore, +selecting a charset filter in the Managed String Library is uni-contextual. +That is to say, there can only be one such filter active for the entire +program, which means its usage is not well defined for intermediate library +usage (a library that uses it will interfere with user code that uses it, and +vice versa.) It is also likely to be poorly defined in multi-threading +environments. + +There is also a question as to whether the data sanitization filter is checked +on every operation, or just on creation operations. Since the charset can be +set arbitrarily at run time, it might be set *after* some managed strings have +been created. This would seem to imply that all functions should run this +additional check every time if there is an attempt to enforce this. This +would make things tremendously slow. On the other hand, if it is assumed that +only creates and other operations that take char *'s as input need be checked +because the charset was only supposed to be called once at and before any +other managed string was created, then one can see that its easy to cover +Bstrlib with equivalent functionality via a few wrapper calls such as the +example given above. + +And finally we have to question the value of sanitation in the first place. +For example, for httpd servers, there is generally a requirement that the +URLs parsed have some form that avoids undesirable translation to local file +system filenames or resources. The problem is that the way URLs can be +encoded, it must be completely parsed and translated to know if it is using +certain invalid character combinations. That is to say, merely filtering +each character one at a time is not necessarily the right way to ensure that +a string has safe contents. + +In the article that describes this proposal, it is claimed that it fairly +closely approximates the existing C API semantics. On this point we should +compare this "closeness" with Bstrlib: + + Bstrlib Managed String Library + ------- ---------------------- + +Pointer arithmetic Segment arithmetic N/A + +Use in C Std lib ->data, or bdata{e} getstr_m(x,*) ... free(x) + +String literals bsStatic, bsStaticBlk strcreate_m() + +Transparency Complete None + +Its pretty clear that the semantic mapping from C strings to Bstrlib is fairly +straightforward, and that in general semantic capabilities are the same or +superior in Bstrlib. On the other hand the Managed String Library is either +missing semantics or changes things fairly significantly. + +Comparison with Annexia's c2lib library +--------------------------------------- + +This library is available at: +http://www.annexia.org/freeware/c2lib + +1. Still based solely on char * buffers (and therefore strlen() and strcat() + is still O(n), and there are no faster streq() comparison functions.) + Their suggestion that alternatives which wrap the string data type (such as + bstring does) imposes a difficulty in interoperating with the C langauge's + ordinary C string library is not founded. +2. Introduction of memory (and vector?) abstractions imposes a learning + curve, and some kind of memory usage policy that is outside of the strings + themselves (and therefore must be maintained by the developer.) +3. The API is massive, and filled with all sorts of trivial (pjoin) and + controvertial (pmatch -- regular expression are not sufficiently + standardized, and there is a very large difference in performance between + compiled and non-compiled, REs) functions. Bstrlib takes a decidely + minimal approach -- none of the functionality in c2lib is difficult or + challenging to implement on top of Bstrlib (except the regex stuff, which + is going to be difficult, and controvertial no matter what.) +4. Understanding why c2lib is the way it is pretty much requires a working + knowledge of Perl. bstrlib requires only knowledge of the C string library + while providing just a very select few worthwhile extras. +5. It is attached to a lot of cruft like a matrix math library (that doesn't + include any functions for getting the determinant, eigenvectors, + eigenvalues, the matrix inverse, test for singularity, test for + orthogonality, a grahm schmit orthogonlization, LU decomposition ... I + mean why bother?) + +Convincing a development house to use c2lib is likely quite difficult. It +introduces too much, while not being part of any kind of standards body. The +code must therefore be trusted, or maintained by those that use it. While +bstring offers nothing more on this front, since its so much smaller, covers +far less in terms of scope, and will typically improve string performance, +the barrier to usage should be much smaller. + +Comparison with stralloc/qmail +------------------------------ + +More information about this library can be found here: +http://www.canonical.org/~kragen/stralloc.html or here: +http://cr.yp.to/lib/stralloc.html + +1. Library is very very minimal. A little too minimal. +2. Untargetted source parameters are not declared const. +3. Slightly different expected emphasis (like _cats function which takes an + ordinary C string char buffer as a parameter.) Its clear that the + remainder of the C string library is still required to perform more + useful string operations. + +The struct declaration for their string header is essentially the same as that +for bstring. But its clear that this was a quickly written hack whose goals +are clearly a subset of what Bstrlib supplies. For anyone who is served by +stralloc, Bstrlib is complete substitute that just adds more functionality. + +stralloc actually uses the interesting policy that a NULL data pointer +indicates an empty string. In this way, non-static empty strings can be +declared without construction. This advantage is minimal, since static empty +bstrings can be declared inline without construction, and if the string needs +to be written to it should be constructed from an empty string (or its first +initializer) in any event. + +wxString class +-------------- + +This is the string class used in the wxWindows project. A description of +wxString can be found here: +http://www.wxwindows.org/manuals/2.4.2/wx368.htm#wxstring + +This C++ library is similar to CBString. However, it is littered with +trivial functions (IsAscii, UpperCase, RemoveLast etc.) + +1. There is no C implementation. +2. The memory management strategy is to allocate a bounded fixed amount of + additional space on each resize, meaning that it does not have the + log_2(n) property that Bstrlib has (it will thrash very easily, cause + massive fragmentation in common heap implementations, and can easily be a + common source of performance problems). +3. The library uses a "copy on write" strategy, meaning that it has to deal + with multithreading problems. + +Vstr +---- + +This is a highly orthogonal C string library with an emphasis on +networking/realtime programming. It can be found here: +http://www.and.org/vstr/ + +1. The convoluted internal structure does not contain a '\0' char * compatible + buffer, so interoperability with the C library a non-starter. +2. The API and implementation is very large (owing to its orthogonality) and + can lead to difficulty in understanding its exact functionality. +3. An obvious dependency on gnu tools (confusing make configure step) +4. Uses a reference counting system, meaning that it is not likely to be + thread safe. + +The implementation has an extreme emphasis on performance for nontrivial +actions (adds, inserts and deletes are all constant or roughly O(#operations) +time) following the "zero copy" principle. This trades off performance of +trivial functions (character access, char buffer access/coersion, alias +detection) which becomes significantly slower, as well as incremental +accumulative costs for its searching/parsing functions. Whether or not Vstr +wins any particular performance benchmark will depend a lot on the benchmark, +but it should handily win on some, while losing dreadfully on others. + +The learning curve for Vstr is very steep, and it doesn't come with any +obvious way to build for Windows or other platforms without gnu tools. At +least one mechanism (the iterator) introduces a new undefined scenario +(writing to a Vstr while iterating through it.) Vstr has a very large +footprint, and is very ambitious in its total functionality. Vstr has no C++ +API. + +Vstr usage requires context initialization via vstr_init() which must be run +in a thread-local context. Given the totally reference based architecture +this means that sharing Vstrings across threads is not well defined, or at +least not safe from race conditions. This API is clearly geared to the older +standard of fork() style multitasking in UNIX, and is not safely transportable +to modern shared memory multithreading available in Linux and Windows. There +is no portable external solution making the library thread safe (since it +requires a mutex around each Vstr context -- not each string.) + +In the documentation for this library, a big deal is made of its self hosted +s(n)printf-like function. This is an issue for older compilers that don't +include vsnprintf(), but also an issue because Vstr has a slow conversion to +'\0' terminated char * mechanism. That is to say, using "%s" to format data +that originates from Vstr would be slow without some sort of native function +to do so. Bstrlib sidesteps the issue by relying on what snprintf-like +functionality does exist and having a high performance conversion to a char * +compatible string so that "%s" can be used directly. + +Str Library +----------- + +This is a fairly extensive string library, that includes full unicode support +and targetted at the goal of out performing MFC and STL. The architecture, +similarly to MFC's CStrings, is a copy on write reference counting mechanism. + +http://www.utilitycode.com/str/default.aspx + +1. Commercial. +2. C++ only. + +This library, like Vstr, uses a ref counting system. There is only so deeply +I can analyze it, since I don't have a license for it. However, performance +improvements over MFC's and STL, doesn't seem like a sufficient reason to +move your source base to it. For example, in the future, Microsoft may +improve the performance CString. + +It should be pointed out that performance testing of Bstrlib has indicated +that its relative performance advantage versus MFC's CString and STL's +std::string is at least as high as that for the Str library. + +libmib astrings +--------------- + +A handful of functional extensions to the C library that add dynamic string +functionality. +http://www.mibsoftware.com/libmib/astring/ + +This package basically references strings through char ** pointers and assumes +they are pointing to the top of an allocated heap entry (or NULL, in which +case memory will be newly allocated from the heap.) So its still up to user +to mix and match the older C string functions with these functions whenever +pointer arithmetic is used (i.e., there is no leveraging of the type system +to assert semantic differences between references and base strings as Bstrlib +does since no new types are introduced.) Unlike Bstrlib, exact string length +meta data is not stored, thus requiring a strlen() call on *every* string +writing operation. The library is very small, covering only a handful of C's +functions. + +While this is better than nothing, it is clearly slower than even the +standard C library, less safe and less functional than Bstrlib. + +To explain the advantage of using libmib, their website shows an example of +how dangerous C code: + + char buf[256]; + char *pszExtraPath = ";/usr/local/bin"; + + strcpy(buf,getenv("PATH")); /* oops! could overrun! */ + strcat(buf,pszExtraPath); /* Could overrun as well! */ + + printf("Checking...%s\n",buf); /* Some printfs overrun too! */ + +is avoided using libmib: + + char *pasz = 0; /* Must initialize to 0 */ + char *paszOut = 0; + char *pszExtraPath = ";/usr/local/bin"; + + if (!astrcpy(&pasz,getenv("PATH"))) /* malloc error */ exit(-1); + if (!astrcat(&pasz,pszExtraPath)) /* malloc error */ exit(-1); + + /* Finally, a "limitless" printf! we can use */ + asprintf(&paszOut,"Checking...%s\n",pasz);fputs(paszOut,stdout); + + astrfree(&pasz); /* Can use free(pasz) also. */ + astrfree(&paszOut); + +However, compare this to Bstrlib: + + bstring b, out; + + bcatcstr (b = bfromcstr (getenv ("PATH")), ";/usr/local/bin"); + out = bformat ("Checking...%s\n", bdatae (b, "")); + /* if (out && b) */ fputs (bdatae (out, ""), stdout); + bdestroy (b); + bdestroy (out); + +Besides being shorter, we can see that error handling can be deferred right +to the very end. Also, unlike the above two versions, if getenv() returns +with NULL, the Bstrlib version will not exhibit undefined behavior. +Initialization starts with the relevant content rather than an extra +autoinitialization step. + +libclc +------ + +An attempt to add to the standard C library with a number of common useful +functions, including additional string functions. +http://libclc.sourceforge.net/ + +1. Uses standard char * buffer, and adopts C 99's usage of "restrict" to pass + the responsibility to guard against aliasing to the programmer. +2. Adds no safety or memory management whatsoever. +3. Most of the supplied string functions are completely trivial. + +The goals of libclc and Bstrlib are clearly quite different. + +fireString +---------- + +http://firestuff.org/ + +1. Uses standard char * buffer, and adopts C 99's usage of "restrict" to pass + the responsibility to guard against aliasing to the programmer. +2. Mixes char * and length wrapped buffers (estr) functions, doubling the API + size, with safety limited to only half of the functions. + +Firestring was originally just a wrapper of char * functionality with extra +length parameters. However, it has been augmented with the inclusion of the +estr type which has similar functionality to stralloc. But firestring does +not nearly cover the functional scope of Bstrlib. + +Safe C String Library +--------------------- + +A library written for the purpose of increasing safety and power to C's string +handling capabilities. +http://www.zork.org/safestr/safestr.html + +1. While the safestr_* functions are safe in of themselves, interoperating + with char * string has dangerous unsafe modes of operation. +2. The architecture of safestr's causes the base pointer to change. Thus, + its not practical/safe to store a safestr in multiple locations if any + single instance can be manipulated. +3. Dependent on an additional error handling library. +4. Uses reference counting, meaning that it is either not thread safe or + slow and not portable. + +I think the idea of reallocating (and hence potentially changing) the base +pointer is a serious design flaw that is fatal to this architecture. True +safety is obtained by having automatic handling of all common scenarios +without creating implicit constraints on the user. + +Because of its automatic temporary clean up system, it cannot use "const" +semantics on input arguments. Interesting anomolies such as: + + safestr_t s, t; + s = safestr_replace (t = SAFESTR_TEMP ("This is a test"), + SAFESTR_TEMP (" "), SAFESTR_TEMP (".")); + /* t is now undefined. */ + +are possible. If one defines a function which takes a safestr_t as a +parameter, then the function would not know whether or not the safestr_t is +defined after it passes it to a safestr library function. The author +recommended method for working around this problem is to examine the +attributes of the safestr_t within the function which is to modify any of +its parameters and play games with its reference count. I think, therefore, +that the whole SAFESTR_TEMP idea is also fatally broken. + +The library implements immutability, optional non-resizability, and a "trust" +flag. This trust flag is interesting, and suggests that applying any +arbitrary sequence of safestr_* function calls on any set of trusted strings +will result in a trusted string. It seems to me, however, that if one wanted +to implement a trusted string semantic, one might do so by actually creating +a different *type* and only implement the subset of string functions that are +deemed safe (i.e., user input would be excluded, for example.) This, in +essence, would allow the compiler to enforce trust propogation at compile +time rather than run time. Non-resizability is also interesting, however, +it seems marginal (i.e., to want a string that cannot be resized, yet can be +modified and yet where a fixed sized buffer is undesirable.) + +=============================================================================== + +Examples +-------- + + Dumping a line numbered file: + + FILE * fp; + int i, ret; + struct bstrList * lines; + struct tagbstring prefix = bsStatic ("-> "); + + if (NULL != (fp = fopen ("bstrlib.txt", "rb"))) { + bstring b = bread ((bNread) fread, fp); + fclose (fp); + if (NULL != (lines = bsplit (b, '\n'))) { + for (i=0; i < lines->qty; i++) { + binsert (lines->entry[i], 0, &prefix, '?'); + printf ("%04d: %s\n", i, bdatae (lines->entry[i], "NULL")); + } + bstrListDestroy (lines); + } + bdestroy (b); + } + +For numerous other examples, see bstraux.c, bstraux.h and the example archive. + +=============================================================================== + +License +------- + +The Better String Library is available under either the 3 clause BSD license +(see the accompanying license.txt) or the Gnu Public License version 2 (see +the accompanying gpl.txt) at the option of the user. + +=============================================================================== + +Acknowledgements +---------------- + +The following individuals have made significant contributions to the design +and testing of the Better String Library: + +Bjorn Augestad +Clint Olsen +Darryl Bleau +Fabian Cenedese +Graham Wideman +Ignacio Burgueno +International Business Machines Corporation +Ira Mica +John Kortink +Manuel Woelker +Marcel van Kervinck +Michael Hsieh +Richard A. Smith +Simon Ekstrom +Wayne Scott + +=============================================================================== diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/license.txt b/Code/Tools/HLSLCrossCompiler/src/cbstring/license.txt new file mode 100644 index 0000000000..cf78a984cc --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/license.txt @@ -0,0 +1,29 @@ +Copyright (c) 2002-2008 Paul Hsieh +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 bstrlib 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 OWNER 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. + diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/porting.txt b/Code/Tools/HLSLCrossCompiler/src/cbstring/porting.txt new file mode 100644 index 0000000000..11d8d13130 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/porting.txt @@ -0,0 +1,172 @@ +Better String library Porting Guide +----------------------------------- + +by Paul Hsieh + +The bstring library is an attempt to provide improved string processing +functionality to the C and C++ language. At the heart of the bstring library +is the management of "bstring"s which are a significant improvement over '\0' +terminated char buffers. See the accompanying documenation file bstrlib.txt +for more information. + +=============================================================================== + +Identifying the Compiler +------------------------ + +Bstrlib has been tested on the following compilers: + + Microsoft Visual C++ + Watcom C/C++ (32 bit flat) + Intel's C/C++ compiler (on Windows) + The GNU C/C++ compiler (on Windows/Linux on x86 and PPC64) + Borland C++ + Turbo C + +There are slight differences in these compilers which requires slight +differences in the implementation of Bstrlib. These are accomodated in the +same sources using #ifdef/#if defined() on compiler specific macros. To +port Bstrlib to a new compiler not listed above, it is recommended that the +same strategy be followed. If you are unaware of the compiler specific +identifying preprocessor macro for your compiler you might find it here: + +http://predef.sourceforge.net/precomp.html + +Note that Intel C/C++ on Windows sets the Microsoft identifier: _MSC_VER. + +16-bit vs. 32-bit vs. 64-bit Systems +------------------------------------ + +Bstrlib has been architected to deal with strings of length between 0 and +INT_MAX (inclusive). Since the values of int are never higher than size_t +there will be no issue here. Note that on most 64-bit systems int is 32-bit. + +Dependency on The C-Library +--------------------------- + +Bstrlib uses the functions memcpy, memmove, malloc, realloc, free and +vsnprintf. Many free standing C compiler implementations that have a mode in +which the C library is not available will typically not include these +functions which will make porting Bstrlib to it onerous. Bstrlib is not +designed for such bare bones compiler environments. This usually includes +compilers that target ROM environments. + +Porting Issues +-------------- + +Bstrlib has been written completely in ANSI/ISO C and ISO C++, however, there +are still a few porting issues. These are described below. + +1. The vsnprintf () function. + +Unfortunately, the earlier ANSI/ISO C standards did not include this function. +If the compiler of interest does not support this function then the +BSTRLIB_NOVSNP should be defined via something like: + + #if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP) + # if defined (__TURBOC__) || defined (__COMPILERVENDORSPECIFICMACRO__) + # define BSTRLIB_NOVSNP + # endif + #endif + +which appears at the top of bstrlib.h. Note that the bformat(a) functions +will not be declared or implemented if the BSTRLIB_NOVSNP macro is set. If +the compiler has renamed vsnprintf() to some other named function, then +search for the definition of the exvsnprintf macro in bstrlib.c file and be +sure its defined appropriately: + + #if defined (__COMPILERVENDORSPECIFICMACRO__) + # define exvsnprintf(r,b,n,f,a) {r=__compiler_specific_vsnprintf(b,n,f,a);} + #else + # define exvsnprintf(r,b,n,f,a) {r=vsnprintf(b,n,f,a);} + #endif + +Take notice of the return value being captured in the variable r. It is +assumed that r exceeds n if and only if the underlying vsnprintf function has +determined what the true maximal output length would be for output if the +buffer were large enough to hold it. Non-modern implementations must output a +lesser number (the macro can and should be modified to ensure this). + +2. Weak C++ compiler. + +C++ is a much more complicated language to implement than C. This has lead +to varying quality of compiler implementations. The weaknesses isolated in +the initial ports are inclusion of the Standard Template Library, +std::iostream and exception handling. By default it is assumed that the C++ +compiler supports all of these things correctly. If your compiler does not +support one or more of these define the corresponding macro: + + BSTRLIB_CANNOT_USE_STL + BSTRLIB_CANNOT_USE_IOSTREAM + BSTRLIB_DOESNT_THROW_EXCEPTIONS + +The compiler specific detected macro should be defined at the top of +bstrwrap.h in the Configuration defines section. Note that these disabling +macros can be overrided with the associated enabling macro if a subsequent +version of the compiler gains support. (For example, its possible to rig +up STLport to provide STL support for WATCOM C/C++, so -DBSTRLIB_CAN_USE_STL +can be passed in as a compiler option.) + +3. The bsafe module, and reserved words. + +The bsafe module is in gross violation of the ANSI/ISO C standard in the +sense that it redefines what could be implemented as reserved words on a +given compiler. The typical problem is that a compiler may inline some of the +functions and thus not be properly overridden by the definitions in the bsafe +module. It is also possible that a compiler may prohibit the redefinitions in +the bsafe module. Compiler specific action will be required to deal with +these situations. + +Platform Specific Files +----------------------- + +The makefiles for the examples are basically setup of for particular +environments for each platform. In general these makefiles are not portable +and should be constructed as necessary from scratch for each platform. + +Testing a port +-------------- + +To test that a port compiles correctly do the following: + +1. Build a sample project that includes the bstrlib, bstraux, bstrwrap, and + bsafe modules. +2. Compile bstest against the bstrlib module. +3. Run bstest and ensure that 0 errors are reported. +4. Compile test against the bstrlib and bstrwrap modules. +5. Run test and ensure that 0 errors are reported. +6. Compile each of the examples (except for the "re" example, which may be + complicated and is not a real test of bstrlib and except for the mfcbench + example which is Windows specific.) +7. Run each of the examples. + +The builds must have 0 errors, and should have the absolute minimum number of +warnings (in most cases can be reduced to 0.) The result of execution should +be essentially identical on each platform. + +Performance +----------- + +Different CPU and compilers have different capabilities in terms of +performance. It is possible for Bstrlib to assume performance +characteristics that a platform doesn't have (since it was primarily +developed on just one platform). The goal of Bstrlib is to provide very good +performance on all platforms regardless of this but without resorting to +extreme measures (such as using assembly language, or non-portable intrinsics +or library extensions.) + +There are two performance benchmarks that can be found in the example/ +directory. They are: cbench.c and cppbench.cpp. These are variations and +expansions of a benchmark for another string library. They don't cover all +string functionality, but do include the most basic functions which will be +common in most string manipulation kernels. + +............................................................................... + +Feedback +-------- + +In all cases, you may email issues found to the primary author of Bstrlib at +the email address: websnarf@users.sourceforge.net + +=============================================================================== diff --git a/Code/Tools/HLSLCrossCompiler/src/cbstring/security.txt b/Code/Tools/HLSLCrossCompiler/src/cbstring/security.txt new file mode 100644 index 0000000000..9761409f56 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/cbstring/security.txt @@ -0,0 +1,221 @@ +Better String library Security Statement +---------------------------------------- + +by Paul Hsieh + +=============================================================================== + +Introduction +------------ + +The Better String library (hereafter referred to as Bstrlib) is an attempt to +provide improved string processing functionality to the C and C++ languages. +At the heart of the Bstrlib is the management of "bstring"s which are a +significant improvement over '\0' terminated char buffers. See the +accompanying documenation file bstrlib.txt for more information. + +DISCLAIMER: 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 OWNER 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. + +Like any software, there is always a possibility of failure due to a flawed +implementation. Nevertheless a good faith effort has been made to minimize +such flaws in Bstrlib. Also, use of Bstrlib by itself will not make an +application secure or free from implementation failures. However, it is the +author's conviction that use of Bstrlib can greatly facilitate the creation +of software meeting the highest possible standards of security. + +Part of the reason why this document has been created, is for the purpose of +security auditing, or the creation of further "Statements on Security" for +software that is created that uses Bstrlib. An auditor may check the claims +below against Bstrlib, and use this as a basis for analysis of software which +uses Bstrlib. + +=============================================================================== + +Statement on Security +--------------------- + +This is a document intended to give consumers of the Better String Library +who are interested in security an idea of where the Better String Library +stands on various security issues. Any deviation observed in the actual +library itself from the descriptions below should be considered an +implementation error, not a design flaw. + +This statement is not an analytical proof of correctness or an outline of one +but rather an assertion similar to a scientific claim or hypothesis. By use, +testing and open independent examination (otherwise known as scientific +falsifiability), the credibility of the claims made below can rise to the +level of an established theory. + +Common security issues: +....................... + +1. Buffer Overflows + +The Bstrlib API allows the programmer a way to deal with strings without +having to deal with the buffers containing them. Ordinary usage of the +Bstrlib API itself makes buffer overflows impossible. + +Furthermore, the Bstrlib API has a superset of basic string functionality as +compared to the C library's char * functions, C++'s std::string class and +Microsoft's MFC based CString class. It also has abstracted mechanisms for +dealing with IO. This is important as it gives developers a way of migrating +all their code from a functionality point of view. + +2. Memory size overflow/wrap around attack + +Bstrlib is, by design, impervious to memory size overflow attacks. The +reason is it is resiliant to length overflows is that bstring lengths are +bounded above by INT_MAX, instead of ~(size_t)0. So length addition +overflows cause a wrap around of the integer value making them negative +causing balloc() to fail before an erroneous operation can occurr. Attempted +conversions of char * strings which may have lengths greater than INT_MAX are +detected and the conversion is aborted. + +It is unknown if this property holds on machines that don't represent +integers as 2s complement. It is recommended that Bstrlib be carefully +auditted by anyone using a system which is not 2s complement based. + +3. Constant string protection + +Bstrlib implements runtime enforced constant and read-only string semantics. +I.e., bstrings which are declared as constant via the bsStatic() macro cannot +be modified or deallocated directly through the Bstrlib API, and this cannot +be subverted by casting or other type coercion. This is independent of the +use of the const_bstring data type. + +The Bstrlib C API uses the type const_bstring to specify bstring parameters +whose contents do not change. Although the C language cannot enforce this, +this is nevertheless guaranteed by the implementation of the Bstrlib library +of C functions. The C++ API enforces the const attribute on CBString types +correctly. + +4. Aliased bstring support + +Bstrlib detects and supports aliased parameter management throughout the API. +The kind of aliasing that is allowed is the one where pointers of the same +basic type may be pointing to overlapping objects (this is the assumption the +ANSI C99 specification makes.) Each function behaves as if all read-only +parameters were copied to temporaries which are used in their stead before +the function is enacted (it rarely actually does this). No function in the +Bstrlib uses the "restrict" parameter attribute from the ANSI C99 +specification. + +5. Information leaking + +In bstraux.h, using the semantically equivalent macros bSecureDestroy() and +bSecureWriteProtect() in place of bdestroy() and bwriteprotect() respectively +will ensure that stale data does not linger in the heap's free space after +strings have been released back to memory. Created bstrings or CBStrings +are not linked to anything external to themselves, and thus cannot expose +deterministic data leaking. If a bstring is resized, the preimage may exist +as a copy that is released to the heap. Thus for sensitive data, the bstring +should be sufficiently presized before manipulated so that it is not resized. +bSecureInput() has been supplied in bstraux.c, which can be used to obtain +input securely without any risk of leaving any part of the input image in the +heap except for the allocated bstring that is returned. + +6. Memory leaking + +Bstrlib can be built using memdbg.h enabled via the BSTRLIB_MEMORY_DEBUG +macro. User generated definitions for malloc, realloc and free can then be +supplied which can implement special strategies for memory corruption +detection or memory leaking. Otherwise, bstrlib does not do anything out of +the ordinary to attempt to deal with the standard problem of memory leaking +(i.e., losing references to allocated memory) when programming in the C and +C++ languages. However, it does not compound the problem any more than exists +either, as it doesn't have any intrinsic inescapable leaks in it. Bstrlib +does not preclude the use of automatic garbage collection mechanisms such as +the Boehm garbage collector. + +7. Encryption + +Bstrlib does not present any built-in encryption mechanism. However, it +supports full binary contents in its data buffers, so any standard block +based encryption mechanism can make direct use of bstrings/CBStrings for +buffer management. + +8. Double freeing + +Freeing a pointer that is already free is an extremely rare, but nevertheless +a potentially ruthlessly corrupting operation (its possible to cause Win 98 to +reboot, by calling free mulitiple times on already freed data using the WATCOM +CRT.) Bstrlib invalidates the bstring header data before freeing, so that in +many cases a double free will be detected and an error will be reported +(though this behaviour is not guaranteed and should not be relied on). + +Using bstrFree pervasively (instead of bdestroy) can lead to somewhat +improved invalid free avoidance (it is completely safe whenever bstring +instances are only stored in unique variables). For example: + + struct tagbstring hw = bsStatic ("Hello, world"); + bstring cpHw = bstrcpy (&hw); + + #ifdef NOT_QUITE_AS_SAFE + bdestroy (cpHw); /* Never fail */ + bdestroy (cpHw); /* Error sometimes detected at runtime */ + bdestroy (&hw); /* Error detected at run time */ + #else + bstrFree (cpHw); /* Never fail */ + bstrFree (cpHw); /* Will do nothing */ + bstrFree (&hw); /* Will lead to a compile time error */ + #endif + +9. Resource based denial of service + +bSecureInput() has been supplied in bstraux.c. It has an optional upper limit +for input length. But unlike fgets(), it is also easily determined if the +buffer has been truncated early. In this way, a program can set an upper limit +on input sizes while still allowing for implementing context specific +truncation semantics (i.e., does the program consume but dump the extra +input, or does it consume it in later inputs?) + +10. Mixing char *'s and bstrings + +The bstring and char * representations are not identical. So there is a risk +when converting back and forth that data may lost. Essentially bstrings can +contain '\0' as a valid non-terminating character, while char * strings +cannot and in fact must use the character as a terminator. The risk of data +loss is very low, since: + + A) the simple method of only using bstrings in a char * semantically + compatible way is both easy to achieve and pervasively supported. + B) obtaining '\0' content in a string is either deliberate or indicative + of another, likely more serious problem in the code. + C) the library comes with various functions which deal with this issue + (namely: bfromcstr(), bstr2cstr (), and bSetCstrChar ()) + +Marginal security issues: +......................... + +11. 8-bit versus 9-bit portability + +Bstrlib uses CHAR_BIT and other limits.h constants to the maximum extent +possible to avoid portability problems. However, Bstrlib has not been tested +on any system that does not represent char as 8-bits. So whether or not it +works on 9-bit systems is an open question. It is recommended that Bstrlib be +carefully auditted by anyone using a system in which CHAR_BIT is not 8. + +12. EBCDIC/ASCII/UTF-8 data representation attacks. + +Bstrlib uses ctype.h functions to ensure that it remains portable to non- +ASCII systems. It also checks range to make sure it is well defined even for +data that ANSI does not define for the ctype functions. + +Obscure issues: +............... + +13. Data attributes + +There is no support for a Perl-like "taint" attribute, however, an example of +how to do this using C++'s type system is given as an example. + diff --git a/Code/Tools/HLSLCrossCompiler/src/decode.c b/Code/Tools/HLSLCrossCompiler/src/decode.c new file mode 100644 index 0000000000..0af6423971 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/decode.c @@ -0,0 +1,1845 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/decode.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/reflect.h" +#include "internal_includes/structs.h" +#include "internal_includes/tokens.h" +#include "stdio.h" +#include "stdlib.h" + +enum +{ + FOURCC_DXBC = FOURCC('D', 'X', 'B', 'C') +}; // DirectX byte code +enum +{ + FOURCC_SHDR = FOURCC('S', 'H', 'D', 'R') +}; // Shader model 4 code +enum +{ + FOURCC_SHEX = FOURCC('S', 'H', 'E', 'X') +}; // Shader model 5 code +enum +{ + FOURCC_RDEF = FOURCC('R', 'D', 'E', 'F') +}; // Resource definition (e.g. constant buffers) +enum +{ + FOURCC_ISGN = FOURCC('I', 'S', 'G', 'N') +}; // Input signature +enum +{ + FOURCC_IFCE = FOURCC('I', 'F', 'C', 'E') +}; // Interface (for dynamic linking) +enum +{ + FOURCC_OSGN = FOURCC('O', 'S', 'G', 'N') +}; // Output signature + +enum +{ + FOURCC_ISG1 = FOURCC('I', 'S', 'G', '1') +}; // Input signature with Stream and MinPrecision +enum +{ + FOURCC_OSG1 = FOURCC('O', 'S', 'G', '1') +}; // Output signature with Stream and MinPrecision +enum +{ + FOURCC_OSG5 = FOURCC('O', 'S', 'G', '5') +}; // Output signature with Stream + +typedef struct DXBCContainerHeaderTAG +{ + unsigned fourcc; + uint32_t unk[4]; + uint32_t one; + uint32_t totalSize; + uint32_t chunkCount; +} DXBCContainerHeader; + +typedef struct DXBCChunkHeaderTAG +{ + unsigned fourcc; + unsigned size; +} DXBCChunkHeader; + +#ifdef _DEBUG +static uint64_t operandID = 0; +static uint64_t instructionID = 0; +#endif + +#if defined(_WIN32) +#define osSprintf(dest, size, src) sprintf_s(dest, size, src) +#else +#define osSprintf(dest, size, src) sprintf(dest, src) +#endif + +void DecodeNameToken(const uint32_t* pui32NameToken, Operand* psOperand) +{ + const size_t MAX_BUFFER_SIZE = sizeof(psOperand->pszSpecialName); + psOperand->eSpecialName = DecodeOperandSpecialName(*pui32NameToken); + switch (psOperand->eSpecialName) + { + case NAME_UNDEFINED: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "undefined"); + break; + } + case NAME_POSITION: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "position"); + break; + } + case NAME_CLIP_DISTANCE: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "clipDistance"); + break; + } + case NAME_CULL_DISTANCE: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "cullDistance"); + break; + } + case NAME_RENDER_TARGET_ARRAY_INDEX: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "renderTargetArrayIndex"); + break; + } + case NAME_VIEWPORT_ARRAY_INDEX: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "viewportArrayIndex"); + break; + } + case NAME_VERTEX_ID: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "vertexID"); + break; + } + case NAME_PRIMITIVE_ID: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "primitiveID"); + break; + } + case NAME_INSTANCE_ID: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "instanceID"); + break; + } + case NAME_IS_FRONT_FACE: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "isFrontFace"); + break; + } + case NAME_SAMPLE_INDEX: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "sampleIndex"); + break; + } + // For the quadrilateral domain, there are 6 factors (4 sides, 2 inner). + case NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR: + case NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR: + + // For the triangular domain, there are 4 factors (3 sides, 1 inner) + case NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_TRI_INSIDE_TESSFACTOR: + + // For the isoline domain, there are 2 factors (detail and density). + case NAME_FINAL_LINE_DETAIL_TESSFACTOR: + case NAME_FINAL_LINE_DENSITY_TESSFACTOR: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "tessFactor"); + break; + } + default: + { + ASSERT(0); + break; + } + } + + return; +} + +uint32_t DecodeOperand(const uint32_t* pui32Tokens, Operand* psOperand) +{ + int i; + uint32_t ui32NumTokens = 1; + OPERAND_NUM_COMPONENTS eNumComponents; + +#ifdef _DEBUG + psOperand->id = operandID++; +#endif + + // Some defaults + psOperand->iWriteMaskEnabled = 1; + psOperand->iGSInput = 0; + psOperand->aeDataType[0] = SVT_FLOAT; + psOperand->aeDataType[1] = SVT_FLOAT; + psOperand->aeDataType[2] = SVT_FLOAT; + psOperand->aeDataType[3] = SVT_FLOAT; + + psOperand->iExtended = DecodeIsOperandExtended(*pui32Tokens); + + psOperand->eModifier = OPERAND_MODIFIER_NONE; + psOperand->psSubOperand[0] = 0; + psOperand->psSubOperand[1] = 0; + psOperand->psSubOperand[2] = 0; + + psOperand->eMinPrecision = OPERAND_MIN_PRECISION_DEFAULT; + + /* Check if this instruction is extended. If it is, + * we need to print the information first */ + if (psOperand->iExtended) + { + /* OperandToken1 is the second token */ + ui32NumTokens++; + + if (DecodeExtendedOperandType(pui32Tokens[1]) == EXTENDED_OPERAND_MODIFIER) + { + psOperand->eModifier = DecodeExtendedOperandModifier(pui32Tokens[1]); + psOperand->eMinPrecision = DecodeOperandMinPrecision(pui32Tokens[1]); + } + } + + psOperand->iIndexDims = DecodeOperandIndexDimension(*pui32Tokens); + psOperand->eType = DecodeOperandType(*pui32Tokens); + + psOperand->ui32RegisterNumber = 0; + + eNumComponents = DecodeOperandNumComponents(*pui32Tokens); + + switch (eNumComponents) + { + case OPERAND_1_COMPONENT: + { + psOperand->iNumComponents = 1; + break; + } + case OPERAND_4_COMPONENT: + { + psOperand->iNumComponents = 4; + break; + } + default: + { + psOperand->iNumComponents = 0; + break; + } + } + + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents == 4) + { + psOperand->eSelMode = DecodeOperand4CompSelMode(*pui32Tokens); + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + psOperand->ui32CompMask = DecodeOperand4CompMask(*pui32Tokens); + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + psOperand->ui32Swizzle = DecodeOperand4CompSwizzle(*pui32Tokens); + + if (psOperand->ui32Swizzle != NO_SWIZZLE) + { + psOperand->aui32Swizzle[0] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 0); + psOperand->aui32Swizzle[1] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 1); + psOperand->aui32Swizzle[2] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 2); + psOperand->aui32Swizzle[3] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 3); + } + else + { + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_X; + psOperand->aui32Swizzle[1] = OPERAND_4_COMPONENT_Y; + psOperand->aui32Swizzle[2] = OPERAND_4_COMPONENT_Z; + psOperand->aui32Swizzle[3] = OPERAND_4_COMPONENT_W; + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psOperand->aui32Swizzle[0] = DecodeOperand4CompSel1(*pui32Tokens); + } + } + + // Set externally to this function based on the instruction opcode. + psOperand->iIntegerImmediate = 0; + + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32) + { + for (i = 0; i < psOperand->iNumComponents; ++i) + { + psOperand->afImmediates[i] = *((float*)(&pui32Tokens[ui32NumTokens])); + ui32NumTokens++; + } + } + else if (psOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + for (i = 0; i < psOperand->iNumComponents; ++i) + { + psOperand->adImmediates[i] = *((double*)(&pui32Tokens[ui32NumTokens])); + ui32NumTokens += 2; + } + } + + if (psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL || psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH) + { + psOperand->ui32RegisterNumber = -1; + psOperand->ui32CompMask = -1; + } + + for (i = 0; i < psOperand->iIndexDims; ++i) + { + OPERAND_INDEX_REPRESENTATION eRep = DecodeOperandIndexRepresentation(i, *pui32Tokens); + + psOperand->eIndexRep[i] = eRep; + + psOperand->aui32ArraySizes[i] = 0; + psOperand->ui32RegisterNumber = 0; + + switch (eRep) + { + case OPERAND_INDEX_IMMEDIATE32: + { + psOperand->ui32RegisterNumber = *(pui32Tokens + ui32NumTokens); + psOperand->aui32ArraySizes[i] = psOperand->ui32RegisterNumber; + break; + } + case OPERAND_INDEX_RELATIVE: + { + psOperand->psSubOperand[i] = hlslcc_malloc(sizeof(Operand)); + DecodeOperand(pui32Tokens + ui32NumTokens, psOperand->psSubOperand[i]); + + ui32NumTokens++; + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + psOperand->ui32RegisterNumber = *(pui32Tokens + ui32NumTokens); + psOperand->aui32ArraySizes[i] = psOperand->ui32RegisterNumber; + + ui32NumTokens++; + + psOperand->psSubOperand[i] = hlslcc_malloc(sizeof(Operand)); + DecodeOperand(pui32Tokens + ui32NumTokens, psOperand->psSubOperand[i]); + + ui32NumTokens++; + break; + } + default: + { + ASSERT(0); + break; + } + } + + ui32NumTokens++; + } + + psOperand->pszSpecialName[0] = '\0'; + + return ui32NumTokens; +} + +const uint32_t* DecodeDeclaration(Shader* psShader, const uint32_t* pui32Token, Declaration* psDecl) +{ + uint32_t ui32TokenLength = DecodeInstructionLength(*pui32Token); + const uint32_t bExtended = DecodeIsOpcodeExtended(*pui32Token); + const OPCODE_TYPE eOpcode = DecodeOpcodeType(*pui32Token); + uint32_t ui32OperandOffset = 1; + + if (eOpcode < NUM_OPCODES && eOpcode >= 0) + { + psShader->aiOpcodeUsed[eOpcode] = 1; + } + + psDecl->eOpcode = eOpcode; + + psDecl->ui32TexReturnType = SVT_FLOAT; + + if (bExtended) + { + ui32OperandOffset = 2; + } + + switch (eOpcode) + { + case OPCODE_DCL_RESOURCE: // DCL* opcodes have + { + ResourceBinding* psBinding = 0; + psDecl->value.eResourceDimension = DecodeResourceDimension(*pui32Token); + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_RESOURCE && + GetResourceFromBindingPoint(RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psBinding)) + { + psDecl->ui32TexReturnType = psBinding->ui32ReturnType; + } + break; + } + case OPCODE_DCL_CONSTANT_BUFFER: // custom operand formats. + { + psDecl->value.eCBAccessPattern = DecodeConstantBufferAccessPattern(*pui32Token); + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_SAMPLER: + { + break; + } + case OPCODE_DCL_INDEX_RANGE: + { + psDecl->ui32NumOperands = 1; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + psDecl->value.ui32IndexRange = pui32Token[ui32OperandOffset]; + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_INPUT) + { + uint32_t i; + const uint32_t indexRange = psDecl->value.ui32IndexRange; + const uint32_t reg = psDecl->asOperands[0].ui32RegisterNumber; + + psShader->aIndexedInput[reg] = indexRange; + psShader->aIndexedInputParents[reg] = reg; + + //-1 means don't declare this input because it falls in + // the range of an already declared array. + for (i = reg + 1; i < reg + indexRange; ++i) + { + psShader->aIndexedInput[i] = -1; + psShader->aIndexedInputParents[i] = reg; + } + } + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_OUTPUT) + { + psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.ui32IndexRange; + } + break; + } + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + { + psDecl->value.eOutputPrimitiveTopology = DecodeGSOutputPrimitiveTopology(*pui32Token); + break; + } + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + { + psDecl->value.eInputPrimitive = DecodeGSInputPrimitive(*pui32Token); + break; + } + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + { + psDecl->value.ui32MaxOutputVertexCount = pui32Token[1]; + break; + } + case OPCODE_DCL_TESS_PARTITIONING: + { + psDecl->value.eTessPartitioning = DecodeTessPartitioning(*pui32Token); + break; + } + case OPCODE_DCL_TESS_DOMAIN: + { + psDecl->value.eTessDomain = DecodeTessDomain(*pui32Token); + break; + } + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + { + psDecl->value.eTessOutPrim = DecodeTessOutPrim(*pui32Token); + break; + } + case OPCODE_DCL_THREAD_GROUP: + { + psDecl->value.aui32WorkGroupSize[0] = pui32Token[1]; + psDecl->value.aui32WorkGroupSize[1] = pui32Token[2]; + psDecl->value.aui32WorkGroupSize[2] = pui32Token[3]; + break; + } + case OPCODE_DCL_INPUT: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_INPUT_SIV: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + if (psShader->eShaderType == PIXEL_SHADER) + { + psDecl->value.eInterpolation = DecodeInterpolationMode(*pui32Token); + } + break; + } + case OPCODE_DCL_INPUT_PS: + { + psDecl->ui32NumOperands = 1; + psDecl->value.eInterpolation = DecodeInterpolationMode(*pui32Token); + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_PS_SGV: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + DecodeNameToken(pui32Token + 3, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_INPUT_PS_SIV: + { + psDecl->ui32NumOperands = 1; + psDecl->value.eInterpolation = DecodeInterpolationMode(*pui32Token); + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + DecodeNameToken(pui32Token + 3, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_OUTPUT: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_OUTPUT_SGV: + { + break; + } + case OPCODE_DCL_OUTPUT_SIV: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + DecodeNameToken(pui32Token + 3, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_TEMPS: + { + psDecl->value.ui32NumTemps = *(pui32Token + ui32OperandOffset); + break; + } + case OPCODE_DCL_INDEXABLE_TEMP: + { + psDecl->sIdxTemp.ui32RegIndex = *(pui32Token + ui32OperandOffset); + psDecl->sIdxTemp.ui32RegCount = *(pui32Token + ui32OperandOffset + 1); + psDecl->sIdxTemp.ui32RegComponentSize = *(pui32Token + ui32OperandOffset + 2); + break; + } + case OPCODE_DCL_GLOBAL_FLAGS: + { + psDecl->value.ui32GlobalFlags = DecodeGlobalFlags(*pui32Token); + break; + } + case OPCODE_DCL_INTERFACE: + { + uint32_t func = 0, numClassesImplementingThisInterface, arrayLen, interfaceID; + interfaceID = pui32Token[ui32OperandOffset]; + ui32OperandOffset++; + psDecl->ui32TableLength = pui32Token[ui32OperandOffset]; + ui32OperandOffset++; + + numClassesImplementingThisInterface = DecodeInterfaceTableLength(*(pui32Token + ui32OperandOffset)); + arrayLen = DecodeInterfaceArrayLength(*(pui32Token + ui32OperandOffset)); + + ui32OperandOffset++; + + psDecl->value.interface.ui32InterfaceID = interfaceID; + psDecl->value.interface.ui32NumFuncTables = numClassesImplementingThisInterface; + psDecl->value.interface.ui32ArraySize = arrayLen; + + psShader->funcPointer[interfaceID].ui32NumBodiesPerTable = psDecl->ui32TableLength; + + for (; func < numClassesImplementingThisInterface; ++func) + { + uint32_t ui32FuncTable = *(pui32Token + ui32OperandOffset); + psShader->aui32FuncTableToFuncPointer[ui32FuncTable] = interfaceID; + + psShader->funcPointer[interfaceID].aui32FuncTables[func] = ui32FuncTable; + ui32OperandOffset++; + } + + break; + } + case OPCODE_DCL_FUNCTION_BODY: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_FUNCTION_TABLE: + { + uint32_t ui32Func; + const uint32_t ui32FuncTableID = pui32Token[ui32OperandOffset++]; + const uint32_t ui32NumFuncsInTable = pui32Token[ui32OperandOffset++]; + + for (ui32Func = 0; ui32Func < ui32NumFuncsInTable; ++ui32Func) + { + const uint32_t ui32FuncBodyID = pui32Token[ui32OperandOffset++]; + + psShader->aui32FuncBodyToFuncTable[ui32FuncBodyID] = ui32FuncTableID; + + psShader->funcTable[ui32FuncTableID].aui32FuncBodies[ui32Func] = ui32FuncBodyID; + } + + // OpcodeToken0 is followed by a DWORD that represents the function table + // identifier and another DWORD (TableLength) that gives the number of + // functions in the table. + // + // This is followed by TableLength DWORDs which are function body indices. + // + + break; + } + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + { + break; + } + case OPCODE_HS_DECLS: + { + break; + } + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + { + psDecl->value.ui32MaxOutputVertexCount = DecodeOutputControlPointCount(*pui32Token); + break; + } + case OPCODE_HS_JOIN_PHASE: + case OPCODE_HS_FORK_PHASE: + case OPCODE_HS_CONTROL_POINT_PHASE: + { + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + ASSERT(psShader->ui32ForkPhaseCount != 0); // Check for wrapping when we decrement. + psDecl->value.aui32HullPhaseInstanceInfo[0] = psShader->ui32ForkPhaseCount - 1; + psDecl->value.aui32HullPhaseInstanceInfo[1] = pui32Token[1]; + break; + } + case OPCODE_CUSTOMDATA: + { + ui32TokenLength = pui32Token[1]; + { + const uint32_t ui32NumVec4 = (ui32TokenLength - 2) / 4; + uint32_t uIdx = 0; + + ICBVec4 const* pVec4Array = (void*)(pui32Token + 2); + + // The buffer will contain at least one value, but not more than 4096 scalars/1024 vec4's. + ASSERT(ui32NumVec4 < MAX_IMMEDIATE_CONST_BUFFER_VEC4_SIZE); + + /* must be a multiple of 4 */ + ASSERT(((ui32TokenLength - 2) % 4) == 0); + + for (uIdx = 0; uIdx < ui32NumVec4; uIdx++) + { + psDecl->asImmediateConstBuffer[uIdx] = pVec4Array[uIdx]; + } + + psDecl->ui32NumOperands = ui32NumVec4; + } + break; + } + case OPCODE_DCL_HS_MAX_TESSFACTOR: + { + psDecl->value.fMaxTessFactor = *((float*)&pui32Token[1]); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + { + psDecl->ui32NumOperands = 2; + psDecl->value.eResourceDimension = DecodeResourceDimension(*pui32Token); + psDecl->sUAV.ui32GloballyCoherentAccess = DecodeAccessCoherencyFlags(*pui32Token); + psDecl->sUAV.bCounter = 0; + psDecl->sUAV.ui32BufferSize = 0; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + psDecl->sUAV.Type = DecodeResourceReturnType(0, pui32Token[ui32OperandOffset]); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + { + + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = DecodeAccessCoherencyFlags(*pui32Token); + psDecl->sUAV.bCounter = 0; + psDecl->sUAV.ui32BufferSize = 0; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + // This should be a RTYPE_UAV_RWBYTEADDRESS buffer. It is memory backed by + // a shader storage buffer whose is unknown at compile time. + psDecl->sUAV.ui32BufferSize = 0; + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + { + ResourceBinding* psBinding = NULL; + ConstantBuffer* psBuffer = NULL; + + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = DecodeAccessCoherencyFlags(*pui32Token); + psDecl->sUAV.bCounter = 0; + psDecl->sUAV.ui32BufferSize = 0; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + GetResourceFromBindingPoint(RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psBinding); + + GetConstantBufferFromBindingPoint(RGROUP_UAV, psBinding->ui32BindPoint, &psShader->sInfo, &psBuffer); + psDecl->sUAV.ui32BufferSize = psBuffer->ui32TotalSizeInBytes; + switch (psBinding->eType) + { + case RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER: + case RTYPE_UAV_APPEND_STRUCTURED: + case RTYPE_UAV_CONSUME_STRUCTURED: + psDecl->sUAV.bCounter = 1; + break; + default: + break; + } + break; + } + case OPCODE_DCL_RESOURCE_STRUCTURED: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_RESOURCE_RAW: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + { + + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = 0; + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + psDecl->sTGSM.ui32Stride = pui32Token[ui32OperandOffset++]; + psDecl->sTGSM.ui32Count = pui32Token[ui32OperandOffset++]; + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW: + { + + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = 0; + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + psDecl->sTGSM.ui32Stride = 4; + psDecl->sTGSM.ui32Count = pui32Token[ui32OperandOffset++] / 4; + break; + } + case OPCODE_DCL_STREAM: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_GS_INSTANCE_COUNT: + { + psDecl->ui32NumOperands = 0; + psDecl->value.ui32GSInstanceCount = pui32Token[1]; + break; + } + default: + { + // Reached end of declarations + return 0; + } + } + + UpdateDeclarationReferences(psShader, psDecl); + + return pui32Token + ui32TokenLength; +} + +const uint32_t* DecodeInstruction(const uint32_t* pui32Token, Instruction* psInst, Shader* psShader) +{ + uint32_t ui32TokenLength = DecodeInstructionLength(*pui32Token); + const uint32_t bExtended = DecodeIsOpcodeExtended(*pui32Token); + const OPCODE_TYPE eOpcode = DecodeOpcodeType(*pui32Token); + uint32_t ui32OperandOffset = 1; + +#ifdef _DEBUG + psInst->id = instructionID++; +#endif + + psInst->eOpcode = eOpcode; + + psInst->bSaturate = DecodeInstructionSaturate(*pui32Token); + + psInst->bAddressOffset = 0; + + psInst->ui32FirstSrc = 1; + + if (bExtended) + { + do + { + const uint32_t ui32ExtOpcodeToken = pui32Token[ui32OperandOffset]; + const EXTENDED_OPCODE_TYPE eExtType = DecodeExtendedOpcodeType(ui32ExtOpcodeToken); + + if (eExtType == EXTENDED_OPCODE_SAMPLE_CONTROLS) + { + psInst->bAddressOffset = 1; + + psInst->iUAddrOffset = DecodeImmediateAddressOffset(IMMEDIATE_ADDRESS_OFFSET_U, ui32ExtOpcodeToken); + psInst->iVAddrOffset = DecodeImmediateAddressOffset(IMMEDIATE_ADDRESS_OFFSET_V, ui32ExtOpcodeToken); + psInst->iWAddrOffset = DecodeImmediateAddressOffset(IMMEDIATE_ADDRESS_OFFSET_W, ui32ExtOpcodeToken); + } + else if (eExtType == EXTENDED_OPCODE_RESOURCE_RETURN_TYPE) + { + psInst->xType = DecodeExtendedResourceReturnType(0, ui32ExtOpcodeToken); + psInst->yType = DecodeExtendedResourceReturnType(1, ui32ExtOpcodeToken); + psInst->zType = DecodeExtendedResourceReturnType(2, ui32ExtOpcodeToken); + psInst->wType = DecodeExtendedResourceReturnType(3, ui32ExtOpcodeToken); + } + else if (eExtType == EXTENDED_OPCODE_RESOURCE_DIM) + { + psInst->eResDim = DecodeExtendedResourceDimension(ui32ExtOpcodeToken); + } + + ui32OperandOffset++; + } while (DecodeIsOpcodeExtended(pui32Token[ui32OperandOffset - 1])); + } + + if (eOpcode < NUM_OPCODES && eOpcode >= 0) + { + psShader->aiOpcodeUsed[eOpcode] = 1; + } + + switch (eOpcode) + { + // no operands + case OPCODE_CUT: + case OPCODE_EMIT: + case OPCODE_EMITTHENCUT: + case OPCODE_RET: + case OPCODE_LOOP: + case OPCODE_ENDLOOP: + case OPCODE_BREAK: + case OPCODE_ELSE: + case OPCODE_ENDIF: + case OPCODE_CONTINUE: + case OPCODE_DEFAULT: + case OPCODE_ENDSWITCH: + case OPCODE_NOP: + case OPCODE_HS_CONTROL_POINT_PHASE: + case OPCODE_HS_FORK_PHASE: + case OPCODE_HS_JOIN_PHASE: + { + psInst->ui32NumOperands = 0; + psInst->ui32FirstSrc = 0; + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + psInst->ui32NumOperands = 0; + psInst->ui32FirstSrc = 0; + break; + } + case OPCODE_SYNC: + { + psInst->ui32NumOperands = 0; + psInst->ui32FirstSrc = 0; + psInst->ui32SyncFlags = DecodeSyncFlags(*pui32Token); + break; + } + + // 1 operand + case OPCODE_EMIT_STREAM: + case OPCODE_CUT_STREAM: + case OPCODE_EMITTHENCUT_STREAM: + case OPCODE_CASE: + case OPCODE_SWITCH: + case OPCODE_LABEL: + { + psInst->ui32NumOperands = 1; + psInst->ui32FirstSrc = 0; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + + // if(eOpcode == OPCODE_CASE) + // { + // psInst->asOperands[0].iIntegerImmediate = 1; + // } + break; + } + + case OPCODE_INTERFACE_CALL: + { + psInst->ui32NumOperands = 1; + psInst->ui32FirstSrc = 0; + psInst->ui32FuncIndexWithinInterface = pui32Token[ui32OperandOffset]; + ui32OperandOffset++; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + + break; + } + + /* Floating point instruction decodes */ + + // Instructions with two operands go here + case OPCODE_MOV: + { + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + + // Mov with an integer dest. If src is an immediate then it must be encoded as an integer. + if (psInst->asOperands[0].eMinPrecision == OPERAND_MIN_PRECISION_SINT_16 || psInst->asOperands[0].eMinPrecision == OPERAND_MIN_PRECISION_UINT_16) + { + psInst->asOperands[1].iIntegerImmediate = 1; + } + break; + } + case OPCODE_LOG: + case OPCODE_RSQ: + case OPCODE_EXP: + case OPCODE_SQRT: + case OPCODE_ROUND_PI: + case OPCODE_ROUND_NI: + case OPCODE_ROUND_Z: + case OPCODE_ROUND_NE: + case OPCODE_FRC: + case OPCODE_FTOU: + case OPCODE_FTOI: + case OPCODE_UTOF: + case OPCODE_ITOF: + case OPCODE_INEG: + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + case OPCODE_DMOV: + case OPCODE_DTOF: + case OPCODE_FTOD: + case OPCODE_DRCP: + case OPCODE_COUNTBITS: + case OPCODE_FIRSTBIT_HI: + case OPCODE_FIRSTBIT_LO: + case OPCODE_FIRSTBIT_SHI: + case OPCODE_BFREV: + case OPCODE_F32TOF16: + case OPCODE_F16TOF32: + case OPCODE_RCP: + case OPCODE_DERIV_RTX: + case OPCODE_DERIV_RTY: + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_NOT: + { + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + break; + } + + // Instructions with three operands go here + case OPCODE_SINCOS: + { + psInst->ui32FirstSrc = 2; + // Intentional fall-through + } + case OPCODE_IMIN: + case OPCODE_UMIN: + case OPCODE_MIN: + case OPCODE_IMAX: + case OPCODE_UMAX: + case OPCODE_MAX: + case OPCODE_MUL: + case OPCODE_DIV: + case OPCODE_ADD: + case OPCODE_DP2: + case OPCODE_DP3: + case OPCODE_DP4: + case OPCODE_NE: + case OPCODE_OR: + case OPCODE_XOR: + case OPCODE_LT: + case OPCODE_IEQ: + case OPCODE_IADD: + case OPCODE_AND: + case OPCODE_GE: + case OPCODE_IGE: + case OPCODE_EQ: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_LD: + case OPCODE_ILT: + case OPCODE_INE: + case OPCODE_ATOMIC_AND: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_IMAX: + case OPCODE_ATOMIC_IMIN: + case OPCODE_DADD: + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DDIV: + { + psInst->ui32NumOperands = 3; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + case OPCODE_UGE: + case OPCODE_ULT: + case OPCODE_USHR: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_UMIN: + { + psInst->ui32NumOperands = 3; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + // Instructions with four operands go here + case OPCODE_MAD: + case OPCODE_MOVC: + case OPCODE_IMAD: + case OPCODE_UDIV: + case OPCODE_LOD: + case OPCODE_SAMPLE: + case OPCODE_GATHER4: + case OPCODE_LD_MS: + case OPCODE_UBFE: + case OPCODE_IBFE: + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_DMOVC: + case OPCODE_DFMA: + case OPCODE_IMUL: + { + psInst->ui32NumOperands = 4; + + if (eOpcode == OPCODE_IMUL || eOpcode == OPCODE_UDIV) + { + psInst->ui32FirstSrc = 2; + } + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + + break; + } + case OPCODE_UADDC: + case OPCODE_USUBB: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + { + psInst->ui32NumOperands = 4; + + if (eOpcode == OPCODE_IMUL) + { + psInst->ui32FirstSrc = 2; + } + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + + break; + } + case OPCODE_GATHER4_PO: + case OPCODE_SAMPLE_L: + case OPCODE_BFI: + case OPCODE_SWAPC: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { + psInst->ui32NumOperands = 5; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[4]); + + break; + } + case OPCODE_GATHER4_C: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_B: + { + psInst->ui32NumOperands = 5; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[4]); + break; + } + case OPCODE_GATHER4_PO_C: + case OPCODE_SAMPLE_D: + { + psInst->ui32NumOperands = 6; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[4]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[5]); + break; + } + case OPCODE_IF: + case OPCODE_BREAKC: + case OPCODE_CONTINUEC: + case OPCODE_RETC: + case OPCODE_DISCARD: + { + psInst->eBooleanTestType = DecodeInstrTestBool(*pui32Token); + psInst->ui32NumOperands = 1; + psInst->ui32FirstSrc = 0; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + break; + } + case OPCODE_CALLC: + { + psInst->eBooleanTestType = DecodeInstrTestBool(*pui32Token); + psInst->ui32NumOperands = 2; + psInst->ui32FirstSrc = 0; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + break; + } + case OPCODE_CUSTOMDATA: + { + psInst->ui32NumOperands = 0; + ui32TokenLength = pui32Token[1]; + break; + } + case OPCODE_EVAL_CENTROID: + { + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + break; + } + case OPCODE_EVAL_SAMPLE_INDEX: + case OPCODE_EVAL_SNAPPED: + case OPCODE_STORE_UAV_TYPED: + case OPCODE_LD_UAV_TYPED: + case OPCODE_LD_RAW: + case OPCODE_STORE_RAW: + { + psInst->ui32NumOperands = 3; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + case OPCODE_STORE_STRUCTURED: + case OPCODE_LD_STRUCTURED: + { + psInst->ui32NumOperands = 4; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + break; + } + case OPCODE_RESINFO: + { + psInst->ui32NumOperands = 3; + + psInst->eResInfoReturnType = DecodeResInfoReturnType(pui32Token[0]); + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + case OPCODE_MSAD: + default: + { + ASSERT(0); + break; + } + } + + UpdateInstructionReferences(psShader, psInst); + + return pui32Token + ui32TokenLength; +} + +void BindTextureToSampler(Shader* psShader, uint32_t ui32TextureRegister, uint32_t ui32SamplerRegister, uint32_t bCompare) +{ + uint32_t ui32Sampler, ui32TextureUnit, bLoad; + ASSERT(ui32TextureRegister < (1 << 10)); + ASSERT(ui32SamplerRegister < (1 << 10)); + + if (psShader->sInfo.ui32NumSamplers >= MAX_RESOURCE_BINDINGS) + { + ASSERT(0); + return; + } + + ui32TextureUnit = ui32TextureRegister; + for (ui32Sampler = 0; ui32Sampler < psShader->sInfo.ui32NumSamplers; ++ui32Sampler) + { + if (psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureBindPoint == ui32TextureRegister) + { + if (psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10SamplerBindPoint == ui32SamplerRegister) + break; + ui32TextureUnit = MAX_RESOURCE_BINDINGS; // Texture is used by two or more samplers - assign to an available texture unit later + } + } + + // MAX_RESOURCE_BINDINGS means no sampler object (used for texture load) + bLoad = ui32SamplerRegister == MAX_RESOURCE_BINDINGS; + + if (bCompare) + psShader->sInfo.asSamplers[ui32Sampler].sMask.bCompareSample = 1; + else if (!bLoad) + psShader->sInfo.asSamplers[ui32Sampler].sMask.bNormalSample = 1; + else + { + psShader->sInfo.asSamplers[ui32Sampler].sMask.bNormalSample = 0; + psShader->sInfo.asSamplers[ui32Sampler].sMask.bCompareSample = 0; + } + + if (ui32Sampler == psShader->sInfo.ui32NumSamplers) + { + psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureBindPoint = ui32TextureRegister; + psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10SamplerBindPoint = ui32SamplerRegister; + psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureUnit = ui32TextureUnit; + ++psShader->sInfo.ui32NumSamplers; + } +} + +void RegisterUniformBuffer(Shader* psShader, ResourceGroup eGroup, uint32_t ui32BindPoint) +{ + uint32_t ui32UniformBuffer = psShader->sInfo.ui32NumUniformBuffers; + psShader->sInfo.asUniformBuffers[ui32UniformBuffer].ui32BindPoint = ui32BindPoint; + psShader->sInfo.asUniformBuffers[ui32UniformBuffer].eGroup = eGroup; + ++psShader->sInfo.ui32NumUniformBuffers; +} + +void RegisterStorageBuffer(Shader* psShader, ResourceGroup eGroup, uint32_t ui32BindPoint) +{ + uint32_t ui32StorageBuffer = psShader->sInfo.ui32NumStorageBuffers; + psShader->sInfo.asStorageBuffers[ui32StorageBuffer].ui32BindPoint = ui32BindPoint; + psShader->sInfo.asStorageBuffers[ui32StorageBuffer].eGroup = eGroup; + ++psShader->sInfo.ui32NumStorageBuffers; +} + +void RegisterImage(Shader* psShader, ResourceGroup eGroup, uint32_t ui32BindPoint) +{ + uint32_t ui32Image = psShader->sInfo.ui32NumImages; + psShader->sInfo.asImages[ui32Image].ui32BindPoint = ui32BindPoint; + psShader->sInfo.asImages[ui32Image].eGroup = eGroup; + ++psShader->sInfo.ui32NumImages; +} + +void AssignRemainingSamplers(Shader* psShader) +{ + uint32_t ui32Sampler; + uint32_t aui32TextureUnitsUsed[(MAX_RESOURCE_BINDINGS + 31) / 32]; + uint32_t ui32MinAvailUnit; + + memset((void*)aui32TextureUnitsUsed, 0, sizeof(aui32TextureUnitsUsed)); + for (ui32Sampler = 0; ui32Sampler < psShader->sInfo.ui32NumSamplers; ++ui32Sampler) + { + uint32_t ui32Unit = psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureUnit; + if (ui32Unit < MAX_RESOURCE_BINDINGS) + aui32TextureUnitsUsed[ui32Unit / 32] |= 1 << (ui32Unit % 32); + } + + ui32MinAvailUnit = 0; + for (ui32Sampler = 0; ui32Sampler < psShader->sInfo.ui32NumSamplers; ++ui32Sampler) + { + uint32_t ui32Unit = psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureUnit; + if (ui32Unit == MAX_RESOURCE_BINDINGS) + { + uint32_t ui32Mask, ui32AvailUnit; + uint32_t ui32WordIndex = ui32MinAvailUnit / 32; + uint32_t ui32BitIndex = ui32MinAvailUnit % 32; + + while (ui32WordIndex < sizeof(aui32TextureUnitsUsed)) + { + if (aui32TextureUnitsUsed[ui32WordIndex] != ~0L) + break; + ++ui32WordIndex; + ui32BitIndex = 0; + } + if (ui32WordIndex == sizeof(aui32TextureUnitsUsed)) + { + ASSERT(0); // Not enough resource bindings + break; + } + + ui32Mask = aui32TextureUnitsUsed[ui32WordIndex]; + while (ui32BitIndex < 32) + { + if ((ui32Mask & (1 << ui32BitIndex)) == 0) + break; + ++ui32BitIndex; + } + if (ui32BitIndex == 32) + { + ASSERT(0); + break; + } + + ui32AvailUnit = 32 * ui32WordIndex + ui32BitIndex; + aui32TextureUnitsUsed[ui32WordIndex] |= (1 << ui32BitIndex); + + psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureUnit = ui32AvailUnit; + ui32MinAvailUnit = ui32AvailUnit + 1; + + ASSERT(psShader->sInfo.asSamplers[ui32Sampler].sMask.ui10TextureUnit < MAX_RESOURCE_BINDINGS); + } + } +} + +void UpdateDeclarationReferences(Shader* psShader, Declaration* psDecl) +{ + switch (psDecl->eOpcode) + { + case OPCODE_DCL_CONSTANT_BUFFER: + RegisterUniformBuffer(psShader, RGROUP_CBUFFER, psDecl->asOperands[0].aui32ArraySizes[0]); + break; + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + RegisterImage(psShader, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber); + break; + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + RegisterStorageBuffer(psShader, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber); + break; + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + RegisterStorageBuffer(psShader, RGROUP_UAV, psDecl->asOperands[0].aui32ArraySizes[0]); + break; + case OPCODE_DCL_RESOURCE_RAW: + RegisterStorageBuffer(psShader, RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber); + break; + case OPCODE_DCL_RESOURCE_STRUCTURED: + RegisterStorageBuffer(psShader, RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber); + break; + } +} + +void UpdateInstructionReferences(Shader* psShader, Instruction* psInst) +{ + uint32_t ui32Operand; + const uint32_t ui32NumOperands = psInst->ui32NumOperands; + for (ui32Operand = 0; ui32Operand < ui32NumOperands; ++ui32Operand) + { + Operand* psOperand = &psInst->asOperands[ui32Operand]; + if (psOperand->eType == OPERAND_TYPE_INPUT || psOperand->eType == OPERAND_TYPE_INPUT_CONTROL_POINT) + { + if (psOperand->iIndexDims == INDEX_2D) + { + if (psOperand->aui32ArraySizes[1] != 0) // gl_in[].gl_Position + { + psShader->abInputReferencedByInstruction[psOperand->ui32RegisterNumber] = 1; + } + } + else + { + psShader->abInputReferencedByInstruction[psOperand->ui32RegisterNumber] = 1; + } + } + } + + switch (psInst->eOpcode) + { + case OPCODE_SWAPC: + psShader->bUseTempCopy = 1; + break; + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_D: + case OPCODE_SAMPLE_B: + case OPCODE_GATHER4: + BindTextureToSampler(psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 0); + break; + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_C: + case OPCODE_GATHER4_C: + BindTextureToSampler(psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1); + break; + case OPCODE_GATHER4_PO: + BindTextureToSampler(psShader, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber, 0); + break; + case OPCODE_GATHER4_PO_C: + BindTextureToSampler(psShader, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber, 1); + break; + case OPCODE_LD: + case OPCODE_LD_MS: + // MAX_RESOURCE_BINDINGS means no sampler object + BindTextureToSampler(psShader, psInst->asOperands[2].ui32RegisterNumber, MAX_RESOURCE_BINDINGS, 0); + break; + } +} + +const uint32_t* DecodeHullShaderJoinPhase(const uint32_t* pui32Tokens, Shader* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + + Instruction* psInst; + + // Declarations + Declaration* psDecl; + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + psShader->psHSJoinPhaseDecl = psDecl; + psShader->ui32HSJoinDeclCount = 0; + + while (1) // Keep going until we reach the first non-declaration token, or the end of the shader. + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + psShader->ui32HSJoinDeclCount++; + psDecl++; + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + // Instructions + psInst = hlslcc_malloc(sizeof(Instruction) * ui32ShaderLength); + psShader->psHSJoinPhaseInstr = psInst; + psShader->ui32HSJoinInstrCount = 0; + + while (pui32CurrentToken < (psShader->pui32FirstToken + ui32ShaderLength)) + { + const uint32_t* nextInstr = DecodeInstruction(pui32CurrentToken, psInst, psShader); + +#ifdef _DEBUG + if (nextInstr == pui32CurrentToken) + { + ASSERT(0); + break; + } +#endif + + pui32CurrentToken = nextInstr; + psShader->ui32HSJoinInstrCount++; + + psInst++; + } + + return pui32CurrentToken; +} + +const uint32_t* DecodeHullShaderForkPhase(const uint32_t* pui32Tokens, Shader* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + const uint32_t ui32ForkPhaseIndex = psShader->ui32ForkPhaseCount; + + Instruction* psInst; + + // Declarations + Declaration* psDecl; + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + + ASSERT(ui32ForkPhaseIndex < MAX_FORK_PHASES); + + psShader->ui32ForkPhaseCount++; + + psShader->apsHSForkPhaseDecl[ui32ForkPhaseIndex] = psDecl; + psShader->aui32HSForkDeclCount[ui32ForkPhaseIndex] = 0; + + while (1) // Keep going until we reach the first non-declaration token, or the end of the shader. + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + psShader->aui32HSForkDeclCount[ui32ForkPhaseIndex]++; + psDecl++; + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + // Instructions + psInst = hlslcc_malloc(sizeof(Instruction) * ui32ShaderLength); + psShader->apsHSForkPhaseInstr[ui32ForkPhaseIndex] = psInst; + psShader->aui32HSForkInstrCount[ui32ForkPhaseIndex] = 0; + + while (pui32CurrentToken < (psShader->pui32FirstToken + ui32ShaderLength)) + { + const uint32_t* nextInstr = DecodeInstruction(pui32CurrentToken, psInst, psShader); + +#ifdef _DEBUG + if (nextInstr == pui32CurrentToken) + { + ASSERT(0); + break; + } +#endif + + pui32CurrentToken = nextInstr; + + if (psInst->eOpcode == OPCODE_HS_FORK_PHASE) + { + pui32CurrentToken = DecodeHullShaderForkPhase(pui32CurrentToken, psShader); + return pui32CurrentToken; + } + + psShader->aui32HSForkInstrCount[ui32ForkPhaseIndex]++; + psInst++; + } + + return pui32CurrentToken; +} + +const uint32_t* DecodeHullShaderControlPointPhase(const uint32_t* pui32Tokens, Shader* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + + Instruction* psInst; + + // TODO one block of memory for instructions and declarions to reduce memory usage and number of allocs. + // hlscc_malloc max(sizeof(declaration), sizeof(instruction) * shader length; or sizeof(DeclInst) - unifying both structs. + + // Declarations + Declaration* psDecl; + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + psShader->psHSControlPointPhaseDecl = psDecl; + psShader->ui32HSControlPointDeclCount = 0; + + while (1) // Keep going until we reach the first non-declaration token, or the end of the shader. + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + psShader->ui32HSControlPointDeclCount++; + psDecl++; + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + // Instructions + psInst = hlslcc_malloc(sizeof(Instruction) * ui32ShaderLength); + psShader->psHSControlPointPhaseInstr = psInst; + psShader->ui32HSControlPointInstrCount = 0; + + while (pui32CurrentToken < (psShader->pui32FirstToken + ui32ShaderLength)) + { + const uint32_t* nextInstr = DecodeInstruction(pui32CurrentToken, psInst, psShader); + +#ifdef _DEBUG + if (nextInstr == pui32CurrentToken) + { + ASSERT(0); + break; + } +#endif + + pui32CurrentToken = nextInstr; + + if (psInst->eOpcode == OPCODE_HS_FORK_PHASE) + { + pui32CurrentToken = DecodeHullShaderForkPhase(pui32CurrentToken, psShader); + return pui32CurrentToken; + } + if (psInst->eOpcode == OPCODE_HS_JOIN_PHASE) + { + pui32CurrentToken = DecodeHullShaderJoinPhase(pui32CurrentToken, psShader); + return pui32CurrentToken; + } + psInst++; + psShader->ui32HSControlPointInstrCount++; + } + + return pui32CurrentToken; +} + +const uint32_t* DecodeHullShader(const uint32_t* pui32Tokens, Shader* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + Declaration* psDecl; + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + psShader->psHSDecl = psDecl; + psShader->ui32HSDeclCount = 0; + + while (1) // Keep going until we reach the first non-declaration token, or the end of the shader. + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + + if (psDecl->eOpcode == OPCODE_HS_CONTROL_POINT_PHASE) + { + pui32CurrentToken = DecodeHullShaderControlPointPhase(pui32CurrentToken, psShader); + return pui32CurrentToken; + } + if (psDecl->eOpcode == OPCODE_HS_FORK_PHASE) + { + pui32CurrentToken = DecodeHullShaderForkPhase(pui32CurrentToken, psShader); + return pui32CurrentToken; + } + if (psDecl->eOpcode == OPCODE_HS_JOIN_PHASE) + { + pui32CurrentToken = DecodeHullShaderJoinPhase(pui32CurrentToken, psShader); + return pui32CurrentToken; + } + + psDecl++; + psShader->ui32HSDeclCount++; + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + return pui32CurrentToken; +} + +void Decode(const uint32_t* pui32Tokens, Shader* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = pui32Tokens[1]; + Instruction* psInst; + Declaration* psDecl; + + psShader->ui32MajorVersion = DecodeProgramMajorVersion(*pui32CurrentToken); + psShader->ui32MinorVersion = DecodeProgramMinorVersion(*pui32CurrentToken); + psShader->eShaderType = DecodeShaderType(*pui32CurrentToken); + + pui32CurrentToken++; // Move to shader length + psShader->ui32ShaderLength = ui32ShaderLength; + pui32CurrentToken++; // Move to after shader length (usually a declaration) + + psShader->pui32FirstToken = pui32Tokens; + +#ifdef _DEBUG + operandID = 0; + instructionID = 0; +#endif + + if (psShader->eShaderType == HULL_SHADER) + { + pui32CurrentToken = DecodeHullShader(pui32CurrentToken, psShader); + return; + } + + // Using ui32ShaderLength as the instruction count + // will allocate more than enough memory. Avoids having to + // traverse the entire shader just to get the real instruction count. + psInst = hlslcc_malloc(sizeof(Instruction) * ui32ShaderLength); + psShader->psInst = psInst; + psShader->ui32InstCount = 0; + + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + psShader->psDecl = psDecl; + psShader->ui32DeclCount = 0; + + while (1) // Keep going until we reach the first non-declaration token, or the end of the shader. + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + psShader->ui32DeclCount++; + psDecl++; + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + while (pui32CurrentToken < (psShader->pui32FirstToken + ui32ShaderLength)) + { + const uint32_t* nextInstr = DecodeInstruction(pui32CurrentToken, psInst, psShader); + +#ifdef _DEBUG + if (nextInstr == pui32CurrentToken) + { + ASSERT(0); + break; + } +#endif + + pui32CurrentToken = nextInstr; + psShader->ui32InstCount++; + psInst++; + } + + AssignRemainingSamplers(psShader); +} + +Shader* DecodeDXBC(uint32_t* data) +{ + Shader* psShader; + DXBCContainerHeader* header = (DXBCContainerHeader*)data; + uint32_t i; + uint32_t chunkCount; + uint32_t* chunkOffsets; + ReflectionChunks refChunks; + uint32_t* shaderChunk = 0; + + if (header->fourcc != FOURCC_DXBC) + { + // Could be SM1/2/3. If the shader type token + // looks valid then we continue + uint32_t type = DecodeShaderTypeDX9(data[0]); + + if (type != INVALID_SHADER) + { + return DecodeDX9BC(data); + } + return 0; + } + + refChunks.pui32Inputs = NULL; + refChunks.pui32Interfaces = NULL; + refChunks.pui32Outputs = NULL; + refChunks.pui32Resources = NULL; + refChunks.pui32Inputs11 = NULL; + refChunks.pui32Outputs11 = NULL; + refChunks.pui32OutputsWithStreams = NULL; + + chunkOffsets = (uint32_t*)(header + 1); + + chunkCount = header->chunkCount; + + for (i = 0; i < chunkCount; ++i) + { + uint32_t offset = chunkOffsets[i]; + + DXBCChunkHeader* chunk = (DXBCChunkHeader*)((char*)data + offset); + + switch (chunk->fourcc) + { + case FOURCC_ISGN: + { + refChunks.pui32Inputs = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_ISG1: + { + refChunks.pui32Inputs11 = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_RDEF: + { + refChunks.pui32Resources = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_IFCE: + { + refChunks.pui32Interfaces = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_OSGN: + { + refChunks.pui32Outputs = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_OSG1: + { + refChunks.pui32Outputs11 = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_OSG5: + { + refChunks.pui32OutputsWithStreams = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_SHDR: + case FOURCC_SHEX: + { + shaderChunk = (uint32_t*)(chunk + 1); + break; + } + default: + { + break; + } + } + } + + if (shaderChunk) + { + uint32_t ui32MajorVersion; + uint32_t ui32MinorVersion; + + psShader = hlslcc_calloc(1, sizeof(Shader)); + + ui32MajorVersion = DecodeProgramMajorVersion(*shaderChunk); + ui32MinorVersion = DecodeProgramMinorVersion(*shaderChunk); + + LoadShaderInfo(ui32MajorVersion, ui32MinorVersion, &refChunks, &psShader->sInfo); + + Decode(shaderChunk, psShader); + + return psShader; + } + + return 0; +} diff --git a/Code/Tools/HLSLCrossCompiler/src/decodeDX9.c b/Code/Tools/HLSLCrossCompiler/src/decodeDX9.c new file mode 100644 index 0000000000..68f4dd6225 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/decodeDX9.c @@ -0,0 +1,1113 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/debug.h" +#include "internal_includes/decode.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/reflect.h" +#include "internal_includes/structs.h" +#include "internal_includes/tokens.h" +#include "stdio.h" +#include "stdlib.h" + +enum +{ + FOURCC_CTAB = FOURCC('C', 'T', 'A', 'B') +}; // Constant table + +#ifdef _DEBUG +static uint64_t dx9operandID = 0; +static uint64_t dx9instructionID = 0; +#endif + +static uint32_t aui32ImmediateConst[256]; +static uint32_t ui32MaxTemp = 0; + +uint32_t DX9_DECODE_OPERAND_IS_SRC = 0x1; +uint32_t DX9_DECODE_OPERAND_IS_DEST = 0x2; +uint32_t DX9_DECODE_OPERAND_IS_DECL = 0x4; + +uint32_t DX9_DECODE_OPERAND_IS_CONST = 0x8; +uint32_t DX9_DECODE_OPERAND_IS_ICONST = 0x10; +uint32_t DX9_DECODE_OPERAND_IS_BCONST = 0x20; + +#define MAX_INPUTS 64 + +static DECLUSAGE_DX9 aeInputUsage[MAX_INPUTS]; +static uint32_t aui32InputUsageIndex[MAX_INPUTS]; + +static void DecodeOperandDX9(const Shader* psShader, const uint32_t ui32Token, const uint32_t ui32Token1, uint32_t ui32Flags, Operand* psOperand) +{ + const uint32_t ui32RegNum = DecodeOperandRegisterNumberDX9(ui32Token); + const uint32_t ui32RegType = DecodeOperandTypeDX9(ui32Token); + const uint32_t bRelativeAddr = DecodeOperandIsRelativeAddressModeDX9(ui32Token); + + const uint32_t ui32WriteMask = DecodeDestWriteMaskDX9(ui32Token); + const uint32_t ui32Swizzle = DecodeOperandSwizzleDX9(ui32Token); + + SHADER_VARIABLE_TYPE ConstType; + + psOperand->ui32RegisterNumber = ui32RegNum; + + psOperand->iNumComponents = 4; + +#ifdef _DEBUG + psOperand->id = dx9operandID++; +#endif + + psOperand->iWriteMaskEnabled = 0; + psOperand->iGSInput = 0; + psOperand->iExtended = 0; + psOperand->psSubOperand[0] = 0; + psOperand->psSubOperand[1] = 0; + psOperand->psSubOperand[2] = 0; + + psOperand->iIndexDims = INDEX_0D; + + psOperand->iIntegerImmediate = 0; + + psOperand->pszSpecialName[0] = '\0'; + + psOperand->eModifier = OPERAND_MODIFIER_NONE; + if (ui32Flags & DX9_DECODE_OPERAND_IS_SRC) + { + uint32_t ui32Modifier = DecodeSrcModifierDX9(ui32Token); + + switch (ui32Modifier) + { + case SRCMOD_DX9_NONE: + { + break; + } + case SRCMOD_DX9_NEG: + { + psOperand->eModifier = OPERAND_MODIFIER_NEG; + break; + } + case SRCMOD_DX9_ABS: + { + psOperand->eModifier = OPERAND_MODIFIER_ABS; + break; + } + case SRCMOD_DX9_ABSNEG: + { + psOperand->eModifier = OPERAND_MODIFIER_ABSNEG; + break; + } + default: + { + ASSERT(0); + break; + } + } + } + + if ((ui32Flags & DX9_DECODE_OPERAND_IS_DECL) == 0) + { + if (ui32Flags & DX9_DECODE_OPERAND_IS_DEST) + { + if (ui32WriteMask != DX9_WRITEMASK_ALL) + { + psOperand->iWriteMaskEnabled = 1; + psOperand->eSelMode = OPERAND_4_COMPONENT_MASK_MODE; + + if (ui32WriteMask & DX9_WRITEMASK_0) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_X; + } + if (ui32WriteMask & DX9_WRITEMASK_1) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_Y; + } + if (ui32WriteMask & DX9_WRITEMASK_2) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_Z; + } + if (ui32WriteMask & DX9_WRITEMASK_3) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_W; + } + } + } + else if (ui32Swizzle != NO_SWIZZLE_DX9) + { + uint32_t component; + + psOperand->iWriteMaskEnabled = 1; + psOperand->eSelMode = OPERAND_4_COMPONENT_SWIZZLE_MODE; + + psOperand->ui32Swizzle = 1; + + /* Add the swizzle */ + if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(0)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_X; + } + else if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(1)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_Y; + } + else if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(2)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_Z; + } + else if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(3)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_W; + } + else + { + for (component = 0; component < 4; component++) + { + uint32_t ui32CompSwiz = ui32Swizzle & (3 << (DX9_SWIZZLE_SHIFT + (component * 2))); + ui32CompSwiz >>= (DX9_SWIZZLE_SHIFT + (component * 2)); + + if (ui32CompSwiz == 0) + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_X; + } + else if (ui32CompSwiz == 1) + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_Y; + } + else if (ui32CompSwiz == 2) + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_Z; + } + else + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_W; + } + } + } + } + + if (bRelativeAddr) + { + psOperand->psSubOperand[0] = hlslcc_malloc(sizeof(Operand)); + DecodeOperandDX9(psShader, ui32Token1, 0, ui32Flags, psOperand->psSubOperand[0]); + + psOperand->iIndexDims = INDEX_1D; + + psOperand->eIndexRep[0] = OPERAND_INDEX_RELATIVE; + + psOperand->aui32ArraySizes[0] = 0; + } + } + + if (ui32RegType == OPERAND_TYPE_DX9_CONSTBOOL) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_BCONST; + ConstType = SVT_BOOL; + } + else if (ui32RegType == OPERAND_TYPE_DX9_CONSTINT) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_ICONST; + ConstType = SVT_INT; + } + else if (ui32RegType == OPERAND_TYPE_DX9_CONST) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_CONST; + ConstType = SVT_FLOAT; + } + + switch (ui32RegType) + { + case OPERAND_TYPE_DX9_TEMP: + { + psOperand->eType = OPERAND_TYPE_TEMP; + + if (ui32MaxTemp < ui32RegNum + 1) + { + ui32MaxTemp = ui32RegNum + 1; + } + break; + } + case OPERAND_TYPE_DX9_INPUT: + { + psOperand->eType = OPERAND_TYPE_INPUT; + + ASSERT(ui32RegNum < MAX_INPUTS); + + if (psShader->eShaderType == PIXEL_SHADER) + { + if (aeInputUsage[ui32RegNum] == DECLUSAGE_TEXCOORD) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_TEXCOORD; + psOperand->ui32RegisterNumber = aui32InputUsageIndex[ui32RegNum]; + } + else + // 0 = base colour, 1 = offset colour. + if (ui32RegNum == 0) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTBASECOLOUR; + } + else + { + ASSERT(ui32RegNum == 1); + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR; + } + } + break; + } + // Same value as OPERAND_TYPE_DX9_TEXCRDOUT + // OPERAND_TYPE_DX9_TEXCRDOUT is the pre-SM3 equivalent + case OPERAND_TYPE_DX9_OUTPUT: + { + psOperand->eType = OPERAND_TYPE_OUTPUT; + + if (psShader->eShaderType == VERTEX_SHADER) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_TEXCOORD; + } + break; + } + case OPERAND_TYPE_DX9_RASTOUT: + { + // RegNum: + // 0=POSIION + // 1=FOG + // 2=POINTSIZE + psOperand->eType = OPERAND_TYPE_OUTPUT; + switch (ui32RegNum) + { + case 0: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_POSITION; + break; + } + case 1: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_FOG; + break; + } + case 2: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_POINTSIZE; + psOperand->iNumComponents = 1; + break; + } + } + break; + } + case OPERAND_TYPE_DX9_ATTROUT: + { + ASSERT(psShader->eShaderType == VERTEX_SHADER); + + psOperand->eType = OPERAND_TYPE_OUTPUT; + + // 0 = base colour, 1 = offset colour. + if (ui32RegNum == 0) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTBASECOLOUR; + } + else + { + ASSERT(ui32RegNum == 1); + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR; + } + + break; + } + case OPERAND_TYPE_DX9_COLOROUT: + { + ASSERT(psShader->eShaderType == PIXEL_SHADER); + psOperand->eType = OPERAND_TYPE_OUTPUT; + break; + } + case OPERAND_TYPE_DX9_CONSTBOOL: + case OPERAND_TYPE_DX9_CONSTINT: + case OPERAND_TYPE_DX9_CONST: + { + // c# = constant float + // i# = constant int + // b# = constant bool + + // c0 might be an immediate while i0 is in the constant buffer + if (aui32ImmediateConst[ui32RegNum] & ui32Flags) + { + if (ConstType != SVT_FLOAT) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_IMMCONSTINT; + } + else + { + psOperand->eType = OPERAND_TYPE_SPECIAL_IMMCONST; + } + } + else + { + psOperand->eType = OPERAND_TYPE_CONSTANT_BUFFER; + psOperand->aui32ArraySizes[1] = psOperand->ui32RegisterNumber; + } + break; + } + case OPERAND_TYPE_DX9_ADDR: + { + // Vertex shader: address register (only have one of these) + // Pixel shader: texture coordinate register (a few of these) + if (psShader->eShaderType == PIXEL_SHADER) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_TEXCOORD; + } + else + { + psOperand->eType = OPERAND_TYPE_SPECIAL_ADDRESS; + } + break; + } + case OPERAND_TYPE_DX9_SAMPLER: + { + psOperand->eType = OPERAND_TYPE_RESOURCE; + break; + } + case OPERAND_TYPE_DX9_LOOP: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_LOOPCOUNTER; + break; + } + default: + { + ASSERT(0); + break; + } + } +} + +static void DeclareNumTemps(Shader* psShader, const uint32_t ui32NumTemps, Declaration* psDecl) +{ + (void)psShader; + + psDecl->eOpcode = OPCODE_DCL_TEMPS; + psDecl->value.ui32NumTemps = ui32NumTemps; +} + +static void SetupRegisterUsage(const Shader* psShader, const uint32_t ui32Token0, const uint32_t ui32Token1) +{ + (void)psShader; + + DECLUSAGE_DX9 eUsage = DecodeUsageDX9(ui32Token0); + uint32_t ui32UsageIndex = DecodeUsageIndexDX9(ui32Token0); + uint32_t ui32RegNum = DecodeOperandRegisterNumberDX9(ui32Token1); + uint32_t ui32RegType = DecodeOperandTypeDX9(ui32Token1); + + if (ui32RegType == OPERAND_TYPE_DX9_INPUT) + { + ASSERT(ui32RegNum < MAX_INPUTS); + aeInputUsage[ui32RegNum] = eUsage; + aui32InputUsageIndex[ui32RegNum] = ui32UsageIndex; + } +} + +// Declaring one constant from a constant buffer will cause all constants in the buffer decalared. +// In dx9 there is only one constant buffer per shader. +static void DeclareConstantBuffer(const Shader* psShader, Declaration* psDecl) +{ + // Pick any constant register in the table. Might not start at c0 (e.g. when register(cX) is used). + uint32_t ui32RegNum = psShader->sInfo.psConstantBuffers->asVars[0].ui32StartOffset / 16; + OPERAND_TYPE_DX9 ui32RegType = OPERAND_TYPE_DX9_CONST; + + if (psShader->sInfo.psConstantBuffers->asVars[0].sType.Type == SVT_INT) + { + ui32RegType = OPERAND_TYPE_DX9_CONSTINT; + } + else if (psShader->sInfo.psConstantBuffers->asVars[0].sType.Type == SVT_BOOL) + { + ui32RegType = OPERAND_TYPE_DX9_CONSTBOOL; + } + + if (psShader->eShaderType == VERTEX_SHADER) + { + psDecl->eOpcode = OPCODE_DCL_INPUT; + } + else + { + psDecl->eOpcode = OPCODE_DCL_INPUT_PS; + } + psDecl->ui32NumOperands = 1; + + DecodeOperandDX9(psShader, CreateOperandTokenDX9(ui32RegNum, ui32RegType), 0, DX9_DECODE_OPERAND_IS_DECL, &psDecl->asOperands[0]); + + ASSERT(psDecl->asOperands[0].eType == OPERAND_TYPE_CONSTANT_BUFFER); + + psDecl->eOpcode = OPCODE_DCL_CONSTANT_BUFFER; + + ASSERT(psShader->sInfo.ui32NumConstantBuffers); + + psDecl->asOperands[0].aui32ArraySizes[0] = 0; // Const buffer index + psDecl->asOperands[0].aui32ArraySizes[1] = psShader->sInfo.psConstantBuffers[0].ui32TotalSizeInBytes / 16; // Number of vec4 constants. +} + +static void DecodeDeclarationDX9(const Shader* psShader, const uint32_t ui32Token0, const uint32_t ui32Token1, Declaration* psDecl) +{ + uint32_t ui32RegType = DecodeOperandTypeDX9(ui32Token1); + + if (psShader->eShaderType == VERTEX_SHADER) + { + psDecl->eOpcode = OPCODE_DCL_INPUT; + } + else + { + psDecl->eOpcode = OPCODE_DCL_INPUT_PS; + } + psDecl->ui32NumOperands = 1; + DecodeOperandDX9(psShader, ui32Token1, 0, DX9_DECODE_OPERAND_IS_DECL, &psDecl->asOperands[0]); + + if (ui32RegType == OPERAND_TYPE_DX9_SAMPLER) + { + const RESOURCE_DIMENSION eResDim = DecodeTextureTypeMaskDX9(ui32Token0); + psDecl->value.eResourceDimension = eResDim; + psDecl->eOpcode = OPCODE_DCL_RESOURCE; + } + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_OUTPUT) + { + psDecl->eOpcode = OPCODE_DCL_OUTPUT; + + if (psDecl->asOperands[0].ui32RegisterNumber == 0 && psShader->eShaderType == VERTEX_SHADER) + { + psDecl->eOpcode = OPCODE_DCL_OUTPUT_SIV; + // gl_Position + psDecl->asOperands[0].eSpecialName = NAME_POSITION; + } + } + else if (psDecl->asOperands[0].eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + psDecl->eOpcode = OPCODE_DCL_CONSTANT_BUFFER; + + ASSERT(psShader->sInfo.ui32NumConstantBuffers); + + psDecl->asOperands[0].aui32ArraySizes[0] = 0; // Const buffer index + psDecl->asOperands[0].aui32ArraySizes[1] = psShader->sInfo.psConstantBuffers[0].ui32TotalSizeInBytes / 16; // Number of vec4 constants. + } +} + +static void DefineDX9(Shader* psShader, + const uint32_t ui32RegNum, + const uint32_t ui32Flags, + const uint32_t c0, + const uint32_t c1, + const uint32_t c2, + const uint32_t c3, + Declaration* psDecl) +{ + (void)psShader; + + psDecl->eOpcode = OPCODE_SPECIAL_DCL_IMMCONST; + psDecl->ui32NumOperands = 2; + + memset(&psDecl->asOperands[0], 0, sizeof(Operand)); + psDecl->asOperands[0].eType = OPERAND_TYPE_SPECIAL_IMMCONST; + + psDecl->asOperands[0].ui32RegisterNumber = ui32RegNum; + + if (ui32Flags & (DX9_DECODE_OPERAND_IS_ICONST | DX9_DECODE_OPERAND_IS_BCONST)) + { + psDecl->asOperands[0].eType = OPERAND_TYPE_SPECIAL_IMMCONSTINT; + } + + aui32ImmediateConst[ui32RegNum] |= ui32Flags; + + memset(&psDecl->asOperands[1], 0, sizeof(Operand)); + psDecl->asOperands[1].eType = OPERAND_TYPE_IMMEDIATE32; + psDecl->asOperands[1].iNumComponents = 4; + psDecl->asOperands[1].iIntegerImmediate = (ui32Flags & (DX9_DECODE_OPERAND_IS_ICONST | DX9_DECODE_OPERAND_IS_BCONST)) ? 1 : 0; + psDecl->asOperands[1].afImmediates[0] = *((float*)&c0); + psDecl->asOperands[1].afImmediates[1] = *((float*)&c1); + psDecl->asOperands[1].afImmediates[2] = *((float*)&c2); + psDecl->asOperands[1].afImmediates[3] = *((float*)&c3); +} + +static void CreateD3D10Instruction(Shader* psShader, + Instruction* psInst, + const OPCODE_TYPE eType, + const uint32_t bHasDest, + const uint32_t ui32SrcCount, + const uint32_t* pui32Tokens) +{ + uint32_t ui32Src; + uint32_t ui32Offset = 1; + + memset(psInst, 0, sizeof(Instruction)); + +#ifdef _DEBUG + psInst->id = dx9instructionID++; +#endif + + psInst->eOpcode = eType; + psInst->ui32NumOperands = ui32SrcCount; + + if (bHasDest) + { + ++psInst->ui32NumOperands; + + DecodeOperandDX9(psShader, pui32Tokens[ui32Offset], pui32Tokens[ui32Offset + 1], DX9_DECODE_OPERAND_IS_DEST, &psInst->asOperands[0]); + + if (DecodeDestModifierDX9(pui32Tokens[ui32Offset]) & DESTMOD_DX9_SATURATE) + { + psInst->bSaturate = 1; + } + + ui32Offset++; + psInst->ui32FirstSrc = 1; + } + + for (ui32Src = 0; ui32Src < ui32SrcCount; ++ui32Src) + { + DecodeOperandDX9(psShader, pui32Tokens[ui32Offset], pui32Tokens[ui32Offset + 1], DX9_DECODE_OPERAND_IS_SRC, &psInst->asOperands[bHasDest + ui32Src]); + + ui32Offset++; + } +} + +Shader* DecodeDX9BC(const uint32_t* pui32Tokens) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + uint32_t ui32NumInstructions = 0; + uint32_t ui32NumDeclarations = 0; + Instruction* psInst; + Declaration* psDecl; + uint32_t decl, inst; + uint32_t bDeclareConstantTable = 0; + Shader* psShader = hlslcc_calloc(1, sizeof(Shader)); + + memset(aui32ImmediateConst, 0, 256); + + psShader->ui32MajorVersion = DecodeProgramMajorVersionDX9(*pui32CurrentToken); + psShader->ui32MinorVersion = DecodeProgramMinorVersionDX9(*pui32CurrentToken); + psShader->eShaderType = DecodeShaderTypeDX9(*pui32CurrentToken); + + pui32CurrentToken++; + + // Work out how many instructions and declarations we need to allocate memory for. + while (1) + { + OPCODE_TYPE_DX9 eOpcode = DecodeOpcodeTypeDX9(pui32CurrentToken[0]); + uint32_t ui32InstLen = DecodeInstructionLengthDX9(pui32CurrentToken[0]); + + if (eOpcode == OPCODE_DX9_END) + { + // SM4+ always end with RET. + // Insert a RET instruction on END to + // replicate this behaviour. + ++ui32NumInstructions; + break; + } + else if (eOpcode == OPCODE_DX9_COMMENT) + { + ui32InstLen = DecodeCommentLengthDX9(pui32CurrentToken[0]); + if (pui32CurrentToken[1] == FOURCC_CTAB) + { + LoadD3D9ConstantTable((char*)(&pui32CurrentToken[2]), &psShader->sInfo); + + ASSERT(psShader->sInfo.ui32NumConstantBuffers); + + if (psShader->sInfo.psConstantBuffers[0].ui32NumVars) + { + ++ui32NumDeclarations; + bDeclareConstantTable = 1; + } + } + } + else if ((eOpcode == OPCODE_DX9_DEF) || (eOpcode == OPCODE_DX9_DEFI) || (eOpcode == OPCODE_DX9_DEFB)) + { + ++ui32NumDeclarations; + } + else if (eOpcode == OPCODE_DX9_DCL) + { + const OPERAND_TYPE_DX9 eType = DecodeOperandTypeDX9(pui32CurrentToken[2]); + uint32_t ignoreDCL = 0; + + // Inputs and outputs are declared in AddVersionDependentCode + if (psShader->eShaderType == PIXEL_SHADER && (OPERAND_TYPE_DX9_CONST != eType && OPERAND_TYPE_DX9_SAMPLER != eType)) + { + ignoreDCL = 1; + } + if (!ignoreDCL) + { + ++ui32NumDeclarations; + } + } + else + { + switch (eOpcode) + { + case OPCODE_DX9_NRM: + { + // Emulate with dp4 and rsq + ui32NumInstructions += 2; + break; + } + default: + { + ++ui32NumInstructions; + break; + } + } + } + + pui32CurrentToken += ui32InstLen + 1; + } + + psInst = hlslcc_malloc(sizeof(Instruction) * ui32NumInstructions); + psShader->psInst = psInst; + psShader->ui32InstCount = ui32NumInstructions; + + if (psShader->eShaderType == VERTEX_SHADER) + { + // Declare gl_Position. vs_3_0 does declare it, SM1/2 do not + ui32NumDeclarations++; + } + + // For declaring temps. + ui32NumDeclarations++; + + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32NumDeclarations); + psShader->psDecl = psDecl; + psShader->ui32DeclCount = ui32NumDeclarations; + + pui32CurrentToken = pui32Tokens + 1; + + inst = 0; + decl = 0; + while (1) + { + OPCODE_TYPE_DX9 eOpcode = DecodeOpcodeTypeDX9(pui32CurrentToken[0]); + uint32_t ui32InstLen = DecodeInstructionLengthDX9(pui32CurrentToken[0]); + + if (eOpcode == OPCODE_DX9_END) + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RET, 0, 0, pui32CurrentToken); + inst++; + break; + } + else if (eOpcode == OPCODE_DX9_COMMENT) + { + ui32InstLen = DecodeCommentLengthDX9(pui32CurrentToken[0]); + } + else if (eOpcode == OPCODE_DX9_DCL) + { + const OPERAND_TYPE_DX9 eType = DecodeOperandTypeDX9(pui32CurrentToken[2]); + uint32_t ignoreDCL = 0; + // Inputs and outputs are declared in AddVersionDependentCode + if (psShader->eShaderType == PIXEL_SHADER && (OPERAND_TYPE_DX9_CONST != eType && OPERAND_TYPE_DX9_SAMPLER != eType)) + { + ignoreDCL = 1; + } + + SetupRegisterUsage(psShader, pui32CurrentToken[1], pui32CurrentToken[2]); + + if (!ignoreDCL) + { + DecodeDeclarationDX9(psShader, pui32CurrentToken[1], pui32CurrentToken[2], &psDecl[decl]); + decl++; + } + } + else if ((eOpcode == OPCODE_DX9_DEF) || (eOpcode == OPCODE_DX9_DEFI) || (eOpcode == OPCODE_DX9_DEFB)) + { + const uint32_t ui32Const0 = *(pui32CurrentToken + 2); + const uint32_t ui32Const1 = *(pui32CurrentToken + 3); + const uint32_t ui32Const2 = *(pui32CurrentToken + 4); + const uint32_t ui32Const3 = *(pui32CurrentToken + 5); + uint32_t ui32Flags = 0; + + if (eOpcode == OPCODE_DX9_DEF) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_CONST; + } + else if (eOpcode == OPCODE_DX9_DEFI) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_ICONST; + } + else + { + ui32Flags |= DX9_DECODE_OPERAND_IS_BCONST; + } + + DefineDX9(psShader, DecodeOperandRegisterNumberDX9(pui32CurrentToken[1]), ui32Flags, ui32Const0, ui32Const1, ui32Const2, ui32Const3, &psDecl[decl]); + decl++; + } + else + { + switch (eOpcode) + { + case OPCODE_DX9_MOV: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MOV, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_LIT: + { + /*Dest.x = 1 + Dest.y = (Src0.x > 0) ? Src0.x : 0 + Dest.z = (Src0.x > 0 && Src0.y > 0) ? pow(Src0.y, Src0.w) : 0 + Dest.w = 1 + */ + ASSERT(0); + break; + } + case OPCODE_DX9_ADD: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ADD, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_SUB: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ADD, 1, 2, pui32CurrentToken); + ASSERT(psInst[inst].asOperands[2].eModifier == OPERAND_MODIFIER_NONE); + psInst[inst].asOperands[2].eModifier = OPERAND_MODIFIER_NEG; + break; + } + case OPCODE_DX9_MAD: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MAD, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_MUL: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MUL, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_RCP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RCP, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_RSQ: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RSQ, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_DP3: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP3, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_DP4: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP4, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_MIN: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MIN, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_MAX: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MAX, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_SLT: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LT, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_SGE: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_GE, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_EXP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_EXP, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_LOG: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LOG, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_NRM: + { + // Convert NRM RESULT, SRCA into: + // dp4 RESULT, SRCA, SRCA + // rsq RESULT, RESULT + + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP4, 1, 1, pui32CurrentToken); + memcpy(&psInst[inst].asOperands[2], &psInst[inst].asOperands[1], sizeof(Operand)); + ++inst; + + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RSQ, 0, 0, pui32CurrentToken); + memcpy(&psInst[inst].asOperands[0], &psInst[inst - 1].asOperands[0], sizeof(Operand)); + break; + } + case OPCODE_DX9_SINCOS: + { + // Before SM3, SINCOS has 2 extra constant sources -D3DSINCOSCONST1 and D3DSINCOSCONST2. + // Ignore them. + + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SINCOS, 1, 1, pui32CurrentToken); + // Pre-SM4: + // If the write mask is .x: dest.x = cos( V ) + // If the write mask is .y: dest.y = sin( V ) + // If the write mask is .xy: + // dest.x = cos( V ) + // dest.y = sin( V ) + + // SM4+ + // destSin destCos Angle + + psInst[inst].ui32NumOperands = 3; + + // Set the angle + memcpy(&psInst[inst].asOperands[2], &psInst[inst].asOperands[1], sizeof(Operand)); + + // Set the cosine dest + memcpy(&psInst[inst].asOperands[1], &psInst[inst].asOperands[0], sizeof(Operand)); + + // Set write masks + psInst[inst].asOperands[0].ui32CompMask &= ~OPERAND_4_COMPONENT_MASK_Y; + if (psInst[inst].asOperands[0].ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + // Need cosine + } + else + { + psInst[inst].asOperands[0].eType = OPERAND_TYPE_NULL; + } + psInst[inst].asOperands[1].ui32CompMask &= ~OPERAND_4_COMPONENT_MASK_X; + if (psInst[inst].asOperands[1].ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + // Need sine + } + else + { + psInst[inst].asOperands[1].eType = OPERAND_TYPE_NULL; + } + + break; + } + case OPCODE_DX9_FRC: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_FRC, 1, 1, pui32CurrentToken); + break; + } + + case OPCODE_DX9_MOVA: + { + // MOVA preforms RoundToNearest on the src data. + // The only rounding functions available in all GLSL version are ceil and floor. + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ROUND_NI, 1, 1, pui32CurrentToken); + break; + } + + case OPCODE_DX9_TEX: + { + // texld r0, t0, s0 + // srcAddress[.swizzle], srcResource[.swizzle], srcSampler + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SAMPLE, 1, 2, pui32CurrentToken); + psInst[inst].asOperands[2].ui32RegisterNumber = 0; + + break; + } + case OPCODE_DX9_TEXLDL: + { + // texld r0, t0, s0 + // srcAddress[.swizzle], srcResource[.swizzle], srcSampler + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SAMPLE_L, 1, 2, pui32CurrentToken); + psInst[inst].asOperands[2].ui32RegisterNumber = 0; + + // Lod comes from fourth coordinate of address. + memcpy(&psInst[inst].asOperands[4], &psInst[inst].asOperands[1], sizeof(Operand)); + + psInst[inst].ui32NumOperands = 5; + + break; + } + + case OPCODE_DX9_IF: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_IF, 0, 1, pui32CurrentToken); + psInst[inst].eDX9TestType = D3DSPC_BOOLEAN; + break; + } + + case OPCODE_DX9_IFC: + { + const COMPARISON_DX9 eCmpOp = DecodeComparisonDX9(pui32CurrentToken[0]); + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_IF, 0, 2, pui32CurrentToken); + psInst[inst].eDX9TestType = eCmpOp; + break; + } + case OPCODE_DX9_ELSE: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ELSE, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_CMP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MOVC, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_REP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_REP, 0, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_ENDREP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ENDREP, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_BREAKC: + { + const COMPARISON_DX9 eCmpOp = DecodeComparisonDX9(pui32CurrentToken[0]); + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_BREAKC, 0, 2, pui32CurrentToken); + psInst[inst].eDX9TestType = eCmpOp; + break; + } + + case OPCODE_DX9_DSX: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DERIV_RTX, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_DSY: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DERIV_RTY, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_TEXKILL: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DISCARD, 1, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_TEXLDD: + { + // texldd, dst, src0, src1, src2, src3 + // srcAddress[.swizzle], srcResource[.swizzle], srcSampler, XGradient, YGradient + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SAMPLE_D, 1, 4, pui32CurrentToken); + psInst[inst].asOperands[2].ui32RegisterNumber = 0; + break; + } + case OPCODE_DX9_LRP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LRP, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_DP2ADD: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP2ADD, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_POW: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_POW, 1, 2, pui32CurrentToken); + break; + } + + case OPCODE_DX9_DST: + case OPCODE_DX9_M4x4: + case OPCODE_DX9_M4x3: + case OPCODE_DX9_M3x4: + case OPCODE_DX9_M3x3: + case OPCODE_DX9_M3x2: + case OPCODE_DX9_CALL: + case OPCODE_DX9_CALLNZ: + case OPCODE_DX9_LABEL: + + case OPCODE_DX9_CRS: + case OPCODE_DX9_SGN: + case OPCODE_DX9_ABS: + + case OPCODE_DX9_TEXCOORD: + case OPCODE_DX9_TEXBEM: + case OPCODE_DX9_TEXBEML: + case OPCODE_DX9_TEXREG2AR: + case OPCODE_DX9_TEXREG2GB: + case OPCODE_DX9_TEXM3x2PAD: + case OPCODE_DX9_TEXM3x2TEX: + case OPCODE_DX9_TEXM3x3PAD: + case OPCODE_DX9_TEXM3x3TEX: + case OPCODE_DX9_TEXM3x3SPEC: + case OPCODE_DX9_TEXM3x3VSPEC: + case OPCODE_DX9_EXPP: + case OPCODE_DX9_LOGP: + case OPCODE_DX9_CND: + case OPCODE_DX9_TEXREG2RGB: + case OPCODE_DX9_TEXDP3TEX: + case OPCODE_DX9_TEXM3x2DEPTH: + case OPCODE_DX9_TEXDP3: + case OPCODE_DX9_TEXM3x3: + case OPCODE_DX9_TEXDEPTH: + case OPCODE_DX9_BEM: + case OPCODE_DX9_SETP: + case OPCODE_DX9_BREAKP: + { + ASSERT(0); + break; + } + case OPCODE_DX9_NOP: + case OPCODE_DX9_PHASE: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_NOP, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_LOOP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LOOP, 0, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_RET: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RET, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_ENDLOOP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ENDLOOP, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_ENDIF: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ENDIF, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_BREAK: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_BREAK, 0, 0, pui32CurrentToken); + break; + } + default: + { + ASSERT(0); + break; + } + } + + UpdateInstructionReferences(psShader, &psInst[inst]); + + inst++; + } + + pui32CurrentToken += ui32InstLen + 1; + } + + DeclareNumTemps(psShader, ui32MaxTemp, &psDecl[decl]); + ++decl; + + if (psShader->eShaderType == VERTEX_SHADER) + { + // Declare gl_Position. vs_3_0 does declare it, SM1/2 do not + if (bDeclareConstantTable) + { + DecodeDeclarationDX9(psShader, 0, CreateOperandTokenDX9(0, OPERAND_TYPE_DX9_RASTOUT), &psDecl[decl + 1]); + } + else + { + DecodeDeclarationDX9(psShader, 0, CreateOperandTokenDX9(0, OPERAND_TYPE_DX9_RASTOUT), &psDecl[decl]); + } + } + + if (bDeclareConstantTable) + { + DeclareConstantBuffer(psShader, &psDecl[decl]); + } + + return psShader; +} diff --git a/Code/Tools/HLSLCrossCompiler/src/hlslccToolkit.c b/Code/Tools/HLSLCrossCompiler/src/hlslccToolkit.c new file mode 100644 index 0000000000..22abd1a5e2 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/hlslccToolkit.c @@ -0,0 +1,167 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +#include "internal_includes/hlslccToolkit.h" +#include "internal_includes/debug.h" +#include "internal_includes/languages.h" + +bool DoAssignmentDataTypesMatch(SHADER_VARIABLE_TYPE dest, SHADER_VARIABLE_TYPE src) +{ + if (src == dest) + return true; + + if ((dest == SVT_FLOAT || dest == SVT_FLOAT10 || dest == SVT_FLOAT16) && + (src == SVT_FLOAT || src == SVT_FLOAT10 || src == SVT_FLOAT16)) + return true; + + if ((dest == SVT_INT || dest == SVT_INT12 || dest == SVT_INT16) && + (src == SVT_INT || src == SVT_INT12 || src == SVT_INT16)) + return true; + + if ((dest == SVT_UINT || dest == SVT_UINT16) && + (src == SVT_UINT || src == SVT_UINT16)) + return true; + + return false; +} + +const char * GetConstructorForTypeGLSL(HLSLCrossCompilerContext* psContext, const SHADER_VARIABLE_TYPE eType, const int components, bool useGLSLPrecision) +{ + const bool usePrecision = useGLSLPrecision && HavePrecisionQualifers(psContext->psShader->eTargetLanguage); + + static const char * const uintTypes[] = { " ", "uint", "uvec2", "uvec3", "uvec4" }; + static const char * const uint16Types[] = { " ", "mediump uint", "mediump uvec2", "mediump uvec3", "mediump uvec4" }; + static const char * const intTypes[] = { " ", "int", "ivec2", "ivec3", "ivec4" }; + static const char * const int16Types[] = { " ", "mediump int", "mediump ivec2", "mediump ivec3", "mediump ivec4" }; + static const char * const int12Types[] = { " ", "lowp int", "lowp ivec2", "lowp ivec3", "lowp ivec4" }; + static const char * const floatTypes[] = { " ", "float", "vec2", "vec3", "vec4" }; + static const char * const float16Types[] = { " ", "mediump float", "mediump vec2", "mediump vec3", "mediump vec4" }; + static const char * const float10Types[] = { " ", "lowp float", "lowp vec2", "lowp vec3", "lowp vec4" }; + static const char * const boolTypes[] = { " ", "bool", "bvec2", "bvec3", "bvec4" }; + + ASSERT(components >= 1 && components <= 4); + + switch (eType) + { + case SVT_UINT: + return uintTypes[components]; + case SVT_UINT16: + return usePrecision ? uint16Types[components] : uintTypes[components]; + case SVT_INT: + return intTypes[components]; + case SVT_INT16: + return usePrecision ? int16Types[components] : intTypes[components]; + case SVT_INT12: + return usePrecision ? int12Types[components] : intTypes[components]; + case SVT_FLOAT: + return floatTypes[components]; + case SVT_FLOAT16: + return usePrecision ? float16Types[components] : floatTypes[components]; + case SVT_FLOAT10: + return usePrecision ? float10Types[components] : floatTypes[components]; + case SVT_BOOL: + return boolTypes[components]; + default: + ASSERT(0); + return ""; + } +} + +SHADER_VARIABLE_TYPE TypeFlagsToSVTType(const uint32_t typeflags) +{ + if (typeflags & TO_FLAG_INTEGER) + return SVT_INT; + if (typeflags & TO_FLAG_UNSIGNED_INTEGER) + return SVT_UINT; + return SVT_FLOAT; +} + +uint32_t SVTTypeToFlag(const SHADER_VARIABLE_TYPE eType) +{ + if (eType == SVT_FLOAT16 || eType == SVT_FLOAT10 || eType == SVT_FLOAT) + { + return TO_FLAG_FLOAT; + } + if (eType == SVT_UINT || eType == SVT_UINT16) + { + return TO_FLAG_UNSIGNED_INTEGER; + } + else if (eType == SVT_INT || eType == SVT_INT16 || eType == SVT_INT12) + { + return TO_FLAG_INTEGER; + } + else + { + return TO_FLAG_NONE; + } +} + +bool CanDoDirectCast(SHADER_VARIABLE_TYPE src, SHADER_VARIABLE_TYPE dest) +{ + // uint<->int<->bool conversions possible + if ((src == SVT_INT || src == SVT_UINT || src == SVT_BOOL || src == SVT_INT12 || src == SVT_INT16 || src == SVT_UINT16) && + (dest == SVT_INT || dest == SVT_UINT || dest == SVT_BOOL || dest == SVT_INT12 || dest == SVT_INT16 || dest == SVT_UINT16)) + return true; + + // float<->double possible + if ((src == SVT_FLOAT || src == SVT_DOUBLE || src == SVT_FLOAT16 || src == SVT_FLOAT10) && + (dest == SVT_FLOAT || dest == SVT_DOUBLE || dest == SVT_FLOAT16 || dest == SVT_FLOAT10)) + return true; + + return false; +} + +const char* GetBitcastOp(SHADER_VARIABLE_TYPE from, SHADER_VARIABLE_TYPE to) +{ + static const char* intToFloat = "intBitsToFloat"; + static const char* uintToFloat = "uintBitsToFloat"; + static const char* floatToInt = "floatBitsToInt"; + static const char* floatToUint = "floatBitsToUint"; + + if ((to == SVT_FLOAT || to == SVT_FLOAT16 || to == SVT_FLOAT10) && from == SVT_INT) + return intToFloat; + else if ((to == SVT_FLOAT || to == SVT_FLOAT16 || to == SVT_FLOAT10) && from == SVT_UINT) + return uintToFloat; + else if (to == SVT_INT && (from == SVT_FLOAT || from == SVT_FLOAT16 || from == SVT_FLOAT10)) + return floatToInt; + else if (to == SVT_UINT && (from == SVT_FLOAT || from == SVT_FLOAT16 || from == SVT_FLOAT10)) + return floatToUint; + + ASSERT(0); + return ""; +} + +bool IsGmemReservedSlot(FRAMEBUFFER_FETCH_TYPE typeMask, const uint32_t regNumber) +{ + if (((typeMask & FBF_ARM_COLOR) && regNumber == GMEM_ARM_COLOR_SLOT) || + ((typeMask & FBF_ARM_DEPTH) && regNumber == GMEM_ARM_DEPTH_SLOT) || + ((typeMask & FBF_ARM_STENCIL) && regNumber == GMEM_ARM_STENCIL_SLOT) || + ((typeMask & FBF_EXT_COLOR) && regNumber >= GMEM_FLOAT_START_SLOT)) + { + return true; + } + + return false; +} + +const char * GetAuxArgumentName(const SHADER_VARIABLE_TYPE varType) +{ + switch (varType) + { + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + return "uArg"; + case SVT_INT: + case SVT_INT16: + case SVT_INT12: + return "iArg"; + case SVT_FLOAT: + case SVT_FLOAT16: + case SVT_FLOAT10: + return "fArg"; + case SVT_BOOL: + return "bArg"; + default: + ASSERT(0); + return ""; + } +} \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/debug.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/debug.h new file mode 100644 index 0000000000..5b071709bc --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/debug.h @@ -0,0 +1,21 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#ifdef _DEBUG +#include "assert.h" +#define ASSERT(expr) CustomAssert(expr) +static void CustomAssert(int expression) +{ + if(!expression) + { + assert(0); + } +} +#else +#define ASSERT(expr) +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/decode.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/decode.h new file mode 100644 index 0000000000..d8102683a8 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/decode.h @@ -0,0 +1,21 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef DECODE_H +#define DECODE_H + +#include "internal_includes/structs.h" + +Shader* DecodeDXBC(uint32_t* data); + +//You don't need to call this directly because DecodeDXBC +//will call DecodeDX9BC if the shader looks +//like it is SM1/2/3. +Shader* DecodeDX9BC(const uint32_t* pui32Tokens); + +void UpdateDeclarationReferences(Shader* psShader, Declaration* psDeclaration); +void UpdateInstructionReferences(Shader* psShader, Instruction* psInstruction); + +#define FOURCC(a, b, c, d) ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24)) + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslccToolkit.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslccToolkit.h new file mode 100644 index 0000000000..d0875613a4 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslccToolkit.h @@ -0,0 +1,35 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +#ifndef HLSLCC_TOOLKIT_DECLARATION_H +#define HLSLCC_TOOLKIT_DECLARATION_H + +#include "hlslcc.h" +#include "bstrlib.h" +#include "internal_includes/structs.h" + +#include + +// Check if "src" type can be assigned directly to the "dest" type. +bool DoAssignmentDataTypesMatch(SHADER_VARIABLE_TYPE dest, SHADER_VARIABLE_TYPE src); + +// Returns the constructor needed depending on the type, the number of components and the use of precision qualifier. +const char * GetConstructorForTypeGLSL(HLSLCrossCompilerContext* psContext, const SHADER_VARIABLE_TYPE eType, const int components, bool useGLSLPrecision); + +// Transform from a variable type to a shader variable flag. +uint32_t SVTTypeToFlag(const SHADER_VARIABLE_TYPE eType); + +// Transform from a shader variable flag to a shader variable type. +SHADER_VARIABLE_TYPE TypeFlagsToSVTType(const uint32_t typeflags); + +// Check if the "src" type can be casted using a constructor to the "dest" type (without bitcasting). +bool CanDoDirectCast(SHADER_VARIABLE_TYPE src, SHADER_VARIABLE_TYPE dest); + +// Returns the bitcast operation needed to assign the "src" type to the "dest" type +const char* GetBitcastOp(SHADER_VARIABLE_TYPE src, SHADER_VARIABLE_TYPE dest); + +// Check if the register number is part of the ones we used for signaling GMEM input +bool IsGmemReservedSlot(FRAMEBUFFER_FETCH_TYPE type, const uint32_t regNumber); + +// Return the name of an auxiliary variable used to save intermediate values to bypass driver issues +const char * GetAuxArgumentName(const SHADER_VARIABLE_TYPE varType); + +#endif \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.c b/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.c new file mode 100644 index 0000000000..0f1c8d62e6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.c @@ -0,0 +1,16 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifdef _WIN32 +#include +#else +#include +#endif +#include + +AZ_PUSH_DISABLE_WARNING(4232, "-Wunknown-warning-option") // address of malloc/free/calloc/realloc are not static +void* (*hlslcc_malloc)(size_t size) = malloc; +void* (*hlslcc_calloc)(size_t num,size_t size) = calloc; +void (*hlslcc_free)(void *p) = free; +void* (*hlslcc_realloc)(void *p,size_t size) = realloc; +AZ_POP_DISABLE_WARNING diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.h new file mode 100644 index 0000000000..533050e17b --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/hlslcc_malloc.h @@ -0,0 +1,15 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef __HLSCC_MALLOC_H +#define __HLSCC_MALLOC_H + +extern void* (*hlslcc_malloc)(size_t size); +extern void* (* hlslcc_calloc)(size_t num, size_t size); +extern void (* hlslcc_free)(void* p); +extern void* (* hlslcc_realloc)(void* p, size_t size); + +#define bstr__alloc hlslcc_malloc +#define bstr__free hlslcc_free +#define bstr__realloc hlslcc_realloc +#endif \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/languages.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/languages.h new file mode 100644 index 0000000000..dd9562379a --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/languages.h @@ -0,0 +1,242 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef LANGUAGES_H +#define LANGUAGES_H + +#include "hlslcc.h" + +static int InOutSupported(const GLLang eLang) +{ + if(eLang == LANG_ES_100 || eLang == LANG_120) + { + return 0; + } + return 1; +} + +static int WriteToFragData(const GLLang eLang) +{ + if(eLang == LANG_ES_100 || eLang == LANG_120) + { + return 1; + } + return 0; +} + +static int ShaderBitEncodingSupported(const GLLang eLang) +{ + if( eLang != LANG_ES_300 && + eLang != LANG_ES_310 && + eLang < LANG_330) + { + return 0; + } + return 1; +} + +static int HaveOverloadedTextureFuncs(const GLLang eLang) +{ + if(eLang == LANG_ES_100 || eLang == LANG_120) + { + return 0; + } + return 1; +} + +//Only enable for ES. +//Not present in 120, ignored in other desktop languages. +static int HavePrecisionQualifers(const GLLang eLang) +{ + if(eLang >= LANG_ES_100 && eLang <= LANG_ES_310) + { + return 1; + } + return 0; +} + +//Only on vertex inputs and pixel outputs. +static int HaveLimitedInOutLocationQualifier(const GLLang eLang) +{ + if(eLang >= LANG_330 || eLang == LANG_ES_300 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveInOutLocationQualifier(const GLLang eLang,const struct GlExtensions *extensions) +{ + if(eLang >= LANG_410 || eLang == LANG_ES_310 || (extensions && ((GlExtensions*)extensions)->ARB_explicit_attrib_location)) + { + return 1; + } + return 0; +} + +//layout(binding = X) uniform {uniformA; uniformB;} +//layout(location = X) uniform uniform_name; +static int HaveUniformBindingsAndLocations(const GLLang eLang,const struct GlExtensions *extensions) +{ + if(eLang >= LANG_430 || eLang == LANG_ES_310 || (extensions && ((GlExtensions*)extensions)->ARB_explicit_uniform_location)) + { + return 1; + } + return 0; +} + +static int DualSourceBlendSupported(const GLLang eLang) +{ + if(eLang >= LANG_330) + { + return 1; + } + return 0; +} + +static int SubroutinesSupported(const GLLang eLang) +{ + if(eLang >= LANG_400) + { + return 1; + } + return 0; +} + +//Before 430, flat/smooth/centroid/noperspective must match +//between fragment and its previous stage. +//HLSL bytecode only tells us the interpolation in pixel shader. +static int PixelInterpDependency(const GLLang eLang) +{ + if(eLang < LANG_430) + { + return 1; + } + return 0; +} + +static int HaveUVec(const GLLang eLang) +{ + switch(eLang) + { + case LANG_ES_100: + case LANG_120: + return 0; + default: + break; + } + return 1; +} + +static int HaveGather(const GLLang eLang) +{ + if(eLang >= LANG_400 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveGatherNonConstOffset(const GLLang eLang) +{ + if(eLang >= LANG_420 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + + +static int HaveQueryLod(const GLLang eLang) +{ + if(eLang >= LANG_400) + { + return 1; + } + return 0; +} + +static int HaveQueryLevels(const GLLang eLang) +{ + if(eLang >= LANG_430) + { + return 1; + } + return 0; +} + + +static int HaveAtomicCounter(const GLLang eLang) +{ + if(eLang >= LANG_420 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveAtomicMem(const GLLang eLang) +{ + if(eLang >= LANG_430) + { + return 1; + } + return 0; +} + +static int HaveCompute(const GLLang eLang) +{ + if(eLang >= LANG_430 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveImageLoadStore(const GLLang eLang) +{ + if(eLang >= LANG_420 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int EmulateDepthClamp(const GLLang eLang) +{ + if (eLang >= LANG_ES_300 && eLang < LANG_120) //Requires gl_FragDepth available in fragment shader + { + return 1; + } + return 0; +} + +static int HaveNoperspectiveInterpolation(const GLLang eLang) +{ + if (eLang >= LANG_330) + { + return 1; + } + return 0; +} + +static int EarlyDepthTestSupported(const GLLang eLang) +{ + if ((eLang > LANG_410) || (eLang == LANG_ES_310)) + { + return 1; + } + return 0; +} + +static int StorageBlockBindingSupported(const GLLang eLang) +{ + if (eLang >= LANG_430) + { + return 1; + } + return 0; +} + + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/reflect.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/reflect.h new file mode 100644 index 0000000000..bea00aafc4 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/reflect.h @@ -0,0 +1,42 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef REFLECT_H +#define REFLECT_H + +#include "hlslcc.h" + +ResourceGroup ResourceTypeToResourceGroup(ResourceType); + +int GetResourceFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ShaderInfo* psShaderInfo, ResourceBinding** ppsOutBinding); + +void GetConstantBufferFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ShaderInfo* psShaderInfo, ConstantBuffer** ppsConstBuf); + +int GetInterfaceVarFromOffset(uint32_t ui32Offset, ShaderInfo* psShaderInfo, ShaderVar** ppsShaderVar); + +int GetInputSignatureFromRegister(const uint32_t ui32Register, const ShaderInfo* psShaderInfo, InOutSignature** ppsOut); +int GetOutputSignatureFromRegister(const uint32_t ui32Register, const uint32_t ui32Stream, const uint32_t ui32CompMask, ShaderInfo* psShaderInfo, InOutSignature** ppsOut); + +int GetOutputSignatureFromSystemValue(SPECIAL_NAME eSystemValueType, uint32_t ui32SemanticIndex, ShaderInfo* psShaderInfo, InOutSignature** ppsOut); + +int GetShaderVarFromOffset(const uint32_t ui32Vec4Offset, const uint32_t* pui32Swizzle, ConstantBuffer* psCBuf, ShaderVarType** ppsShaderVar, int32_t* pi32Index, int32_t* pi32Rebase); + +typedef struct +{ + uint32_t* pui32Inputs; + uint32_t* pui32Outputs; + uint32_t* pui32Resources; + uint32_t* pui32Interfaces; + uint32_t* pui32Inputs11; + uint32_t* pui32Outputs11; + uint32_t* pui32OutputsWithStreams; +} ReflectionChunks; + +void LoadShaderInfo(const uint32_t ui32MajorVersion, const uint32_t ui32MinorVersion, const ReflectionChunks* psChunks, ShaderInfo* psInfo); + +void LoadD3D9ConstantTable(const char* data, ShaderInfo* psInfo); + +void FreeShaderInfo(ShaderInfo* psShaderInfo); + +#endif + diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/shaderLimits.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/shaderLimits.h new file mode 100644 index 0000000000..7bddbed4da --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/shaderLimits.h @@ -0,0 +1,36 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef HLSLCC_SHADER_LIMITS_H +#define HLSLCC_SHADER_LIMITS_H + +static enum +{ + MAX_SHADER_VEC4_OUTPUT = 512 +}; +static enum +{ + MAX_SHADER_VEC4_INPUT = 512 +}; +static enum +{ + MAX_TEXTURES = 128 +}; +static enum +{ + MAX_FORK_PHASES = 2 +}; +static enum +{ + MAX_FUNCTION_BODIES = 1024 +}; +static enum +{ + MAX_CLASS_TYPES = 1024 +}; +static enum +{ + MAX_FUNCTION_POINTERS = 128 +}; + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/structs.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/structs.h new file mode 100644 index 0000000000..a9e7fd92b7 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/structs.h @@ -0,0 +1,374 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef STRUCTS_H +#define STRUCTS_H + +#include "hlslcc.h" +#include "bstrlib.h" + +#include "internal_includes/tokens.h" +#include "internal_includes/reflect.h" + +enum +{ + MAX_SUB_OPERANDS = 3 +}; + +typedef struct Operand_TAG +{ + int iExtended; + OPERAND_TYPE eType; + OPERAND_MODIFIER eModifier; + OPERAND_MIN_PRECISION eMinPrecision; + int iIndexDims; + int indexRepresentation[4]; + int writeMask; + int iGSInput; + int iWriteMaskEnabled; + + int iNumComponents; + + OPERAND_4_COMPONENT_SELECTION_MODE eSelMode; + uint32_t ui32CompMask; + uint32_t ui32Swizzle; + uint32_t aui32Swizzle[4]; + + uint32_t aui32ArraySizes[3]; + uint32_t ui32RegisterNumber; + //If eType is OPERAND_TYPE_IMMEDIATE32 + float afImmediates[4]; + //If eType is OPERAND_TYPE_IMMEDIATE64 + double adImmediates[4]; + + int iIntegerImmediate; + + SPECIAL_NAME eSpecialName; + char pszSpecialName[64]; + + OPERAND_INDEX_REPRESENTATION eIndexRep[3]; + + struct Operand_TAG* psSubOperand[MAX_SUB_OPERANDS]; + + //One type for each component. + SHADER_VARIABLE_TYPE aeDataType[4]; + +#ifdef _DEBUG + uint64_t id; +#endif +} Operand; + +typedef struct Instruction_TAG +{ + OPCODE_TYPE eOpcode; + INSTRUCTION_TEST_BOOLEAN eBooleanTestType; + COMPARISON_DX9 eDX9TestType; + uint32_t ui32SyncFlags; + uint32_t ui32NumOperands; + uint32_t ui32FirstSrc; + Operand asOperands[6]; + uint32_t bSaturate; + uint32_t ui32FuncIndexWithinInterface; + RESINFO_RETURN_TYPE eResInfoReturnType; + + int bAddressOffset; + int iUAddrOffset; + int iVAddrOffset; + int iWAddrOffset; + RESOURCE_RETURN_TYPE xType, yType, zType, wType; + RESOURCE_DIMENSION eResDim; + +#ifdef _DEBUG + uint64_t id; +#endif +} Instruction; + +enum +{ + MAX_IMMEDIATE_CONST_BUFFER_VEC4_SIZE = 1024 +}; + +typedef struct ICBVec4_TAG +{ + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; +} ICBVec4; + +typedef struct Declaration_TAG +{ + OPCODE_TYPE eOpcode; + + uint32_t ui32NumOperands; + + Operand asOperands[2]; + + ICBVec4 asImmediateConstBuffer[MAX_IMMEDIATE_CONST_BUFFER_VEC4_SIZE]; + //The declaration can set one of these + //values depending on the opcode. + union + { + uint32_t ui32GlobalFlags; + uint32_t ui32NumTemps; + RESOURCE_DIMENSION eResourceDimension; + CONSTANT_BUFFER_ACCESS_PATTERN eCBAccessPattern; + INTERPOLATION_MODE eInterpolation; + PRIMITIVE_TOPOLOGY eOutputPrimitiveTopology; + PRIMITIVE eInputPrimitive; + uint32_t ui32MaxOutputVertexCount; + TESSELLATOR_DOMAIN eTessDomain; + TESSELLATOR_PARTITIONING eTessPartitioning; + TESSELLATOR_OUTPUT_PRIMITIVE eTessOutPrim; + uint32_t aui32WorkGroupSize[3]; + //Fork phase index followed by the instance count. + uint32_t aui32HullPhaseInstanceInfo[2]; + float fMaxTessFactor; + uint32_t ui32IndexRange; + uint32_t ui32GSInstanceCount; + + struct Interface_TAG + { + uint32_t ui32InterfaceID; + uint32_t ui32NumFuncTables; + uint32_t ui32ArraySize; + } interface; + } value; + + struct UAV_TAG + { + uint32_t ui32GloballyCoherentAccess; + uint32_t ui32BufferSize; + uint8_t bCounter; + RESOURCE_RETURN_TYPE Type; + } sUAV; + + struct TGSM_TAG + { + uint32_t ui32Stride; + uint32_t ui32Count; + } sTGSM; + + struct IndexableTemp_TAG + { + uint32_t ui32RegIndex; + uint32_t ui32RegCount; + uint32_t ui32RegComponentSize; + } sIdxTemp; + + uint32_t ui32TableLength; + + uint32_t ui32TexReturnType; +} Declaration; + +enum +{ + MAX_TEMP_VEC4 = 512 +}; + +enum +{ + MAX_GROUPSHARED = 8 +}; + +enum +{ + MAX_DX9_IMMCONST = 256 +}; + +typedef struct Shader_TAG +{ + uint32_t ui32MajorVersion; + uint32_t ui32MinorVersion; + SHADER_TYPE eShaderType; + + GLLang eTargetLanguage; + const struct GlExtensions *extensions; + + int fp64; + + //DWORDs in program code, including version and length tokens. + uint32_t ui32ShaderLength; + + uint32_t ui32DeclCount; + Declaration* psDecl; + + //Instruction* functions;//non-main subroutines + + uint32_t aui32FuncTableToFuncPointer[MAX_FUNCTION_TABLES];//FIXME dynamic alloc + uint32_t aui32FuncBodyToFuncTable[MAX_FUNCTION_BODIES]; + + struct + { + uint32_t aui32FuncBodies[MAX_FUNCTION_BODIES]; + }funcTable[MAX_FUNCTION_TABLES]; + + struct + { + uint32_t aui32FuncTables[MAX_FUNCTION_TABLES]; + uint32_t ui32NumBodiesPerTable; + }funcPointer[MAX_FUNCTION_POINTERS]; + + uint32_t ui32NextClassFuncName[MAX_CLASS_TYPES]; + + uint32_t ui32InstCount; + Instruction* psInst; + + const uint32_t* pui32FirstToken;//Reference for calculating current position in token stream. + + //Hull shader declarations and instructions. + //psDecl, psInst are null for hull shaders. + uint32_t ui32HSDeclCount; + Declaration* psHSDecl; + + uint32_t ui32HSControlPointDeclCount; + Declaration* psHSControlPointPhaseDecl; + + uint32_t ui32HSControlPointInstrCount; + Instruction* psHSControlPointPhaseInstr; + + uint32_t ui32ForkPhaseCount; + + uint32_t aui32HSForkDeclCount[MAX_FORK_PHASES]; + Declaration* apsHSForkPhaseDecl[MAX_FORK_PHASES]; + + uint32_t aui32HSForkInstrCount[MAX_FORK_PHASES]; + Instruction* apsHSForkPhaseInstr[MAX_FORK_PHASES]; + + uint32_t ui32HSJoinDeclCount; + Declaration* psHSJoinPhaseDecl; + + uint32_t ui32HSJoinInstrCount; + Instruction* psHSJoinPhaseInstr; + + ShaderInfo sInfo; + + int abScalarInput[MAX_SHADER_VEC4_INPUT]; + + int aIndexedOutput[MAX_SHADER_VEC4_OUTPUT]; + + int aIndexedInput[MAX_SHADER_VEC4_INPUT]; + int aIndexedInputParents[MAX_SHADER_VEC4_INPUT]; + + RESOURCE_DIMENSION aeResourceDims[MAX_TEXTURES]; + + int aiInputDeclaredSize[MAX_SHADER_VEC4_INPUT]; + + int aiOutputDeclared[MAX_SHADER_VEC4_OUTPUT]; + + //Does not track built-in inputs. + int abInputReferencedByInstruction[MAX_SHADER_VEC4_INPUT]; + + int aiOpcodeUsed[NUM_OPCODES]; + + uint32_t ui32CurrentVertexOutputStream; + + uint32_t ui32NumDx9ImmConst; + uint32_t aui32Dx9ImmConstArrayRemap[MAX_DX9_IMMCONST]; + + ShaderVarType sGroupSharedVarType[MAX_GROUPSHARED]; + + SHADER_VARIABLE_TYPE aeCommonTempVecType[MAX_TEMP_VEC4]; + uint32_t bUseTempCopy; + FRAMEBUFFER_FETCH_TYPE eGmemType; +} Shader; + +/* CONFETTI NOTE: DAVID SROUR + * The following is super sketchy, but at the moment, + * there is no way to figure out the type of a resource + * since HLSL has only register sets for the following: + * bool, int4, float4, sampler. + * THIS CODE IS DUPLICATED FROM HLSLcc METAL. + * IF ANYTHING CHANGES, BOTH TRANSLATORS SHOULD HAVE THE CHANGE. + * TODO: CONSOLIDATE THE 2 HLSLcc PROJECTS. + */ +enum +{ + GMEM_FLOAT4_START_SLOT = 120 +}; +enum +{ + GMEM_FLOAT3_START_SLOT = 112 +}; +enum +{ + GMEM_FLOAT2_START_SLOT = 104 +}; +enum +{ + GMEM_FLOAT_START_SLOT = 96 +}; + +enum +{ + GMEM_ARM_COLOR_SLOT = 93, + GMEM_ARM_DEPTH_SLOT = 94, + GMEM_ARM_STENCIL_SLOT = 95 +}; + +/* CONFETTI NOTE: DAVID SROUR + * Following is the reserved slot for PLS extension (https://www.khronos.org/registry/gles/extensions/EXT/EXT_shader_pixel_local_storage.txt). + * It will get picked up when a RWStructuredBuffer resource is defined at the following reserved slot. + * Note that only one PLS struct can be present at a time otherwise the behavior is undefined. + * + * Types in the struct and their output conversion (each output variable will always be 4 bytes): + * float2 -> rg16f + * float3 -> r11f_g11f_b10f + * float4 -> rgba8 + * uint -> r32ui + * int2 -> rg16i + * int4 -> rgba8i + */ +enum +{ + GMEM_PLS_RO_SLOT = 60 +}; // READ-ONLY +enum +{ + GMEM_PLS_WO_SLOT = 61 +}; // WRITE-ONLY +enum +{ + GMEM_PLS_RW_SLOT = 62 +}; // READ/WRITE + +static const uint32_t MAIN_PHASE = 0; +static const uint32_t HS_FORK_PHASE = 1; +static const uint32_t HS_CTRL_POINT_PHASE = 2; +static const uint32_t HS_JOIN_PHASE = 3; +enum +{ + NUM_PHASES = 4 +}; + +enum +{ + MAX_COLOR_MRT = 8 +}; + +enum +{ + INPUT_RENDERTARGET = 1 << 0, + OUTPUT_RENDERTARGET = 1 << 1 +}; + +typedef struct HLSLCrossCompilerContext_TAG +{ + bstring glsl; + bstring earlyMain;//Code to be inserted at the start of main() + bstring postShaderCode[NUM_PHASES];//End of main or before emit() + bstring debugHeader; + + bstring* currentGLSLString;//either glsl or earlyMain + + int havePostShaderCode[NUM_PHASES]; + uint32_t currentPhase; + + uint32_t rendertargetUse[MAX_COLOR_MRT]; + + int indent; + unsigned int flags; + Shader* psShader; +} HLSLCrossCompilerContext; + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLDeclaration.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLDeclaration.h new file mode 100644 index 0000000000..337a771e19 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLDeclaration.h @@ -0,0 +1,19 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_GLSL_DECLARATION_H +#define TO_GLSL_DECLARATION_H + +#include "internal_includes/structs.h" + +void TranslateDeclaration(HLSLCrossCompilerContext* psContext, const Declaration* psDecl); + +char* GetDeclaredInputName(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand); +char* GetDeclaredOutputName(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand, int* stream); + +//Hull shaders have multiple phases. +//Each phase has its own temps. +//Convert to global temps for GLSL. +void ConsolidateHullTempVars(Shader* psShader); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLInstruction.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLInstruction.h new file mode 100644 index 0000000000..bf6795d931 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLInstruction.h @@ -0,0 +1,18 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_GLSL_INSTRUCTION_H +#define TO_GLSL_INSTRUCTION_H + +#include "internal_includes/structs.h" + +void TranslateInstruction(HLSLCrossCompilerContext* psContext, Instruction* psInst); + +//For each MOV temp, immediate; check to see if the next instruction +//using that temp has an integer opcode. If so then the immediate value +//is flaged as having an integer encoding. +void MarkIntegerImmediates(HLSLCrossCompilerContext* psContext); + +void SetDataTypes(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount, SHADER_VARIABLE_TYPE* aeCommonTempVecType); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLOperand.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLOperand.h new file mode 100644 index 0000000000..56487ea69b --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toGLSLOperand.h @@ -0,0 +1,46 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_GLSL_OPERAND_H +#define TO_GLSL_OPERAND_H + +#include "internal_includes/structs.h" + +void TranslateOperand(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag); + +int GetMaxComponentFromComponentMask(const Operand* psOperand); +void TranslateOperandIndex(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index); +void TranslateOperandIndexMAD(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add); +void TranslateVariableName(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle); +void TranslateOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand); + +uint32_t GetNumSwizzleElements(const Operand* psOperand); +void AddSwizzleUsingElementCount(HLSLCrossCompilerContext* psContext, uint32_t count); +int GetFirstOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +uint32_t IsSwizzleReplacated(const Operand* psOperand); + +void TextureName(bstring output, Shader* psShader, const uint32_t ui32TextureRegister, const uint32_t ui32SamplerRegister, const int bCompare); +void UAVName(bstring output, Shader* psShader, const uint32_t ui32RegisterNumber); +void UniformBufferName(bstring output, Shader* psShader, const uint32_t ui32RegisterNumber); + +void ConvertToTextureName(bstring output, Shader* psShader, const char* szName, const char* szSamplerName, const int bCompare); +void ConvertToUAVName(bstring output, Shader* psShader, const char* szOriginalUAVName); +void ConvertToUniformBufferName(bstring output, Shader* psShader, const char* szConstantBufferName); + +void ShaderVarName(bstring output, Shader* psShader, const char* OriginalName); +void ShaderVarFullName(bstring output, Shader* psShader, const ShaderVarType* psShaderVar); + +uint32_t ConvertOperandSwizzleToComponentMask(const Operand* psOperand); +//Non-zero means the components overlap +int CompareOperandSwizzles(const Operand* psOperandA, const Operand* psOperandB); + +SHADER_VARIABLE_TYPE GetOperandDataType(HLSLCrossCompilerContext* psContext, const Operand* psOperand); + + +// NOTE: CODE DUPLICATION FROM HLSLcc METAL //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void TranslateGmemOperandSwizzleWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask, uint32_t gmemNumElements); +uint32_t GetGmemInputResourceSlot(uint32_t const slotIn); +uint32_t GetGmemInputResourceNumElements(uint32_t const slotIn); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALDeclaration.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALDeclaration.h new file mode 100644 index 0000000000..724723bf49 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALDeclaration.h @@ -0,0 +1,16 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_METAL_DECLARATION_H +#define TO_METAL_DECLARATION_H + +#include "internal_includes/structs.h" + +void TranslateDeclarationMETAL(HLSLCrossCompilerContext* psContext, const Declaration* psDecl); + +char* GetDeclaredInputNameMETAL(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand); +char* GetDeclaredOutputNameMETAL(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand); + +const char* GetMangleSuffixMETAL(const SHADER_TYPE eShaderType); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALInstruction.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALInstruction.h new file mode 100644 index 0000000000..eb29e74685 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALInstruction.h @@ -0,0 +1,18 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_METAL_INSTRUCTION_H +#define TO_METAL_INSTRUCTION_H + +#include "internal_includes/structs.h" + +void TranslateInstructionMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst); + +//For each MOV temp, immediate; check to see if the next instruction +//using that temp has an integer opcode. If so then the immediate value +//is flaged as having an integer encoding. +void MarkIntegerImmediatesMETAL(HLSLCrossCompilerContext* psContext); + +void SetDataTypesMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount, SHADER_VARIABLE_TYPE* aeCommonTempVecType); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALOperand.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALOperand.h new file mode 100644 index 0000000000..d4bcbcbc73 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/toMETALOperand.h @@ -0,0 +1,38 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_METAL_OPERAND_H +#define TO_METAL_OPERAND_H + +#include "internal_includes/structs.h" + +#define TO_FLAG_NONE 0x0 +#define TO_FLAG_INTEGER 0x1 +#define TO_FLAG_NAME_ONLY 0x2 +#define TO_FLAG_DECLARATION_NAME 0x4 +#define TO_FLAG_DESTINATION 0x8 //Operand is being written to by assignment. +#define TO_FLAG_UNSIGNED_INTEGER 0x10 +#define TO_FLAG_DOUBLE 0x20 +#define TO_FLAG_FLOAT 0x40 + +void TranslateOperandMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag); + +int GetMaxComponentFromComponentMaskMETAL(const Operand* psOperand); +void TranslateOperandMETALIndex(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index); +void TranslateOperandMETALIndexMAD(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add); +void TranslateVariableNameMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle); +void TranslateOperandMETALSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +uint32_t GetNumSwizzleElementsMETAL(const Operand* psOperand); +void AddSwizzleUsingElementCountMETAL(HLSLCrossCompilerContext* psContext, uint32_t count); +int GetFirstOperandSwizzleMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +uint32_t IsSwizzleReplacatedMETAL(const Operand* psOperand); + +void TextureNameMETAL(HLSLCrossCompilerContext* psContext, const uint32_t ui32RegisterNumber, const int bZCompare); + +uint32_t ConvertOperandSwizzleToComponentMaskMETAL(const Operand* psOperand); +//Non-zero means the components overlap +int CompareOperandSwizzlesMETAL(const Operand* psOperandA, const Operand* psOperandB); + +SHADER_VARIABLE_TYPE GetOperandDataTypeMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand); + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/tokens.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/tokens.h new file mode 100644 index 0000000000..635edf57be --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/tokens.h @@ -0,0 +1,812 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TOKENS_H +#define TOKENS_H + +#include "hlslcc.h" + +typedef enum +{ + INVALID_SHADER = -1, + PIXEL_SHADER, + VERTEX_SHADER, + GEOMETRY_SHADER, + HULL_SHADER, + DOMAIN_SHADER, + COMPUTE_SHADER, +} SHADER_TYPE; + +static SHADER_TYPE DecodeShaderType(uint32_t ui32Token) +{ + return (SHADER_TYPE)((ui32Token & 0xffff0000) >> 16); +} + +static uint32_t DecodeProgramMajorVersion(uint32_t ui32Token) +{ + return (ui32Token & 0x000000f0) >> 4; +} + +static uint32_t DecodeProgramMinorVersion(uint32_t ui32Token) +{ + return (ui32Token & 0x0000000f); +} + +static uint32_t DecodeInstructionLength(uint32_t ui32Token) +{ + return (ui32Token & 0x7f000000) >> 24; +} + +static uint32_t DecodeIsOpcodeExtended(uint32_t ui32Token) +{ + return (ui32Token & 0x80000000) >> 31; +} + +typedef enum EXTENDED_OPCODE_TYPE +{ + EXTENDED_OPCODE_EMPTY = 0, + EXTENDED_OPCODE_SAMPLE_CONTROLS = 1, + EXTENDED_OPCODE_RESOURCE_DIM = 2, + EXTENDED_OPCODE_RESOURCE_RETURN_TYPE = 3, +} EXTENDED_OPCODE_TYPE; + +static EXTENDED_OPCODE_TYPE DecodeExtendedOpcodeType(uint32_t ui32Token) +{ + return (EXTENDED_OPCODE_TYPE)(ui32Token & 0x0000003f); +} + +typedef enum RESOURCE_RETURN_TYPE +{ + RETURN_TYPE_UNORM = 1, + RETURN_TYPE_SNORM = 2, + RETURN_TYPE_SINT = 3, + RETURN_TYPE_UINT = 4, + RETURN_TYPE_FLOAT = 5, + RETURN_TYPE_MIXED = 6, + RETURN_TYPE_DOUBLE = 7, + RETURN_TYPE_CONTINUED = 8, + RETURN_TYPE_UNUSED = 9, +} RESOURCE_RETURN_TYPE; + +static RESOURCE_RETURN_TYPE DecodeResourceReturnType(uint32_t ui32Coord, uint32_t ui32Token) +{ + return (RESOURCE_RETURN_TYPE)((ui32Token>>(ui32Coord * 4))&0xF); +} + +static RESOURCE_RETURN_TYPE DecodeExtendedResourceReturnType(uint32_t ui32Coord, uint32_t ui32Token) +{ + return (RESOURCE_RETURN_TYPE)((ui32Token>>(ui32Coord * 4 + 6))&0xF); +} + +typedef enum +{ + //For DX9 + OPCODE_POW = -6, + OPCODE_DP2ADD = -5, + OPCODE_LRP = -4, + OPCODE_ENDREP = -3, + OPCODE_REP = -2, + OPCODE_SPECIAL_DCL_IMMCONST = -1, + + OPCODE_ADD, + OPCODE_AND, + OPCODE_BREAK, + OPCODE_BREAKC, + OPCODE_CALL, + OPCODE_CALLC, + OPCODE_CASE, + OPCODE_CONTINUE, + OPCODE_CONTINUEC, + OPCODE_CUT, + OPCODE_DEFAULT, + OPCODE_DERIV_RTX, + OPCODE_DERIV_RTY, + OPCODE_DISCARD, + OPCODE_DIV, + OPCODE_DP2, + OPCODE_DP3, + OPCODE_DP4, + OPCODE_ELSE, + OPCODE_EMIT, + OPCODE_EMITTHENCUT, + OPCODE_ENDIF, + OPCODE_ENDLOOP, + OPCODE_ENDSWITCH, + OPCODE_EQ, + OPCODE_EXP, + OPCODE_FRC, + OPCODE_FTOI, + OPCODE_FTOU, + OPCODE_GE, + OPCODE_IADD, + OPCODE_IF, + OPCODE_IEQ, + OPCODE_IGE, + OPCODE_ILT, + OPCODE_IMAD, + OPCODE_IMAX, + OPCODE_IMIN, + OPCODE_IMUL, + OPCODE_INE, + OPCODE_INEG, + OPCODE_ISHL, + OPCODE_ISHR, + OPCODE_ITOF, + OPCODE_LABEL, + OPCODE_LD, + OPCODE_LD_MS, + OPCODE_LOG, + OPCODE_LOOP, + OPCODE_LT, + OPCODE_MAD, + OPCODE_MIN, + OPCODE_MAX, + OPCODE_CUSTOMDATA, + OPCODE_MOV, + OPCODE_MOVC, + OPCODE_MUL, + OPCODE_NE, + OPCODE_NOP, + OPCODE_NOT, + OPCODE_OR, + OPCODE_RESINFO, + OPCODE_RET, + OPCODE_RETC, + OPCODE_ROUND_NE, + OPCODE_ROUND_NI, + OPCODE_ROUND_PI, + OPCODE_ROUND_Z, + OPCODE_RSQ, + OPCODE_SAMPLE, + OPCODE_SAMPLE_C, + OPCODE_SAMPLE_C_LZ, + OPCODE_SAMPLE_L, + OPCODE_SAMPLE_D, + OPCODE_SAMPLE_B, + OPCODE_SQRT, + OPCODE_SWITCH, + OPCODE_SINCOS, + OPCODE_UDIV, + OPCODE_ULT, + OPCODE_UGE, + OPCODE_UMUL, + OPCODE_UMAD, + OPCODE_UMAX, + OPCODE_UMIN, + OPCODE_USHR, + OPCODE_UTOF, + OPCODE_XOR, + OPCODE_DCL_RESOURCE, // DCL* opcodes have + OPCODE_DCL_CONSTANT_BUFFER, // custom operand formats. + OPCODE_DCL_SAMPLER, + OPCODE_DCL_INDEX_RANGE, + OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY, + OPCODE_DCL_GS_INPUT_PRIMITIVE, + OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT, + OPCODE_DCL_INPUT, + OPCODE_DCL_INPUT_SGV, + OPCODE_DCL_INPUT_SIV, + OPCODE_DCL_INPUT_PS, + OPCODE_DCL_INPUT_PS_SGV, + OPCODE_DCL_INPUT_PS_SIV, + OPCODE_DCL_OUTPUT, + OPCODE_DCL_OUTPUT_SGV, + OPCODE_DCL_OUTPUT_SIV, + OPCODE_DCL_TEMPS, + OPCODE_DCL_INDEXABLE_TEMP, + OPCODE_DCL_GLOBAL_FLAGS, + + // ----------------------------------------------- + + OPCODE_RESERVED_10, + + // ---------- DX 10.1 op codes--------------------- + + OPCODE_LOD, + OPCODE_GATHER4, + OPCODE_SAMPLE_POS, + OPCODE_SAMPLE_INFO, + + // ----------------------------------------------- + + // This should be 10.1's version of NUM_OPCODES + OPCODE_RESERVED_10_1, + + // ---------- DX 11 op codes--------------------- + OPCODE_HS_DECLS, // token marks beginning of HS sub-shader + OPCODE_HS_CONTROL_POINT_PHASE, // token marks beginning of HS sub-shader + OPCODE_HS_FORK_PHASE, // token marks beginning of HS sub-shader + OPCODE_HS_JOIN_PHASE, // token marks beginning of HS sub-shader + + OPCODE_EMIT_STREAM, + OPCODE_CUT_STREAM, + OPCODE_EMITTHENCUT_STREAM, + OPCODE_INTERFACE_CALL, + + OPCODE_BUFINFO, + OPCODE_DERIV_RTX_COARSE, + OPCODE_DERIV_RTX_FINE, + OPCODE_DERIV_RTY_COARSE, + OPCODE_DERIV_RTY_FINE, + OPCODE_GATHER4_C, + OPCODE_GATHER4_PO, + OPCODE_GATHER4_PO_C, + OPCODE_RCP, + OPCODE_F32TOF16, + OPCODE_F16TOF32, + OPCODE_UADDC, + OPCODE_USUBB, + OPCODE_COUNTBITS, + OPCODE_FIRSTBIT_HI, + OPCODE_FIRSTBIT_LO, + OPCODE_FIRSTBIT_SHI, + OPCODE_UBFE, + OPCODE_IBFE, + OPCODE_BFI, + OPCODE_BFREV, + OPCODE_SWAPC, + + OPCODE_DCL_STREAM, + OPCODE_DCL_FUNCTION_BODY, + OPCODE_DCL_FUNCTION_TABLE, + OPCODE_DCL_INTERFACE, + + OPCODE_DCL_INPUT_CONTROL_POINT_COUNT, + OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT, + OPCODE_DCL_TESS_DOMAIN, + OPCODE_DCL_TESS_PARTITIONING, + OPCODE_DCL_TESS_OUTPUT_PRIMITIVE, + OPCODE_DCL_HS_MAX_TESSFACTOR, + OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT, + OPCODE_DCL_HS_JOIN_PHASE_INSTANCE_COUNT, + + OPCODE_DCL_THREAD_GROUP, + OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED, + OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW, + OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED, + OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW, + OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED, + OPCODE_DCL_RESOURCE_RAW, + OPCODE_DCL_RESOURCE_STRUCTURED, + OPCODE_LD_UAV_TYPED, + OPCODE_STORE_UAV_TYPED, + OPCODE_LD_RAW, + OPCODE_STORE_RAW, + OPCODE_LD_STRUCTURED, + OPCODE_STORE_STRUCTURED, + OPCODE_ATOMIC_AND, + OPCODE_ATOMIC_OR, + OPCODE_ATOMIC_XOR, + OPCODE_ATOMIC_CMP_STORE, + OPCODE_ATOMIC_IADD, + OPCODE_ATOMIC_IMAX, + OPCODE_ATOMIC_IMIN, + OPCODE_ATOMIC_UMAX, + OPCODE_ATOMIC_UMIN, + OPCODE_IMM_ATOMIC_ALLOC, + OPCODE_IMM_ATOMIC_CONSUME, + OPCODE_IMM_ATOMIC_IADD, + OPCODE_IMM_ATOMIC_AND, + OPCODE_IMM_ATOMIC_OR, + OPCODE_IMM_ATOMIC_XOR, + OPCODE_IMM_ATOMIC_EXCH, + OPCODE_IMM_ATOMIC_CMP_EXCH, + OPCODE_IMM_ATOMIC_IMAX, + OPCODE_IMM_ATOMIC_IMIN, + OPCODE_IMM_ATOMIC_UMAX, + OPCODE_IMM_ATOMIC_UMIN, + OPCODE_SYNC, + + OPCODE_DADD, + OPCODE_DMAX, + OPCODE_DMIN, + OPCODE_DMUL, + OPCODE_DEQ, + OPCODE_DGE, + OPCODE_DLT, + OPCODE_DNE, + OPCODE_DMOV, + OPCODE_DMOVC, + OPCODE_DTOF, + OPCODE_FTOD, + + OPCODE_EVAL_SNAPPED, + OPCODE_EVAL_SAMPLE_INDEX, + OPCODE_EVAL_CENTROID, + + OPCODE_DCL_GS_INSTANCE_COUNT, + + OPCODE_ABORT, + OPCODE_DEBUG_BREAK, + + // ----------------------------------------------- + + // This marks the end of D3D11.0 opcodes + OPCODE_RESERVED_11, + + OPCODE_DDIV, + OPCODE_DFMA, + OPCODE_DRCP, + + OPCODE_MSAD, + + OPCODE_DTOI, + OPCODE_DTOU, + OPCODE_ITOD, + OPCODE_UTOD, + + // ----------------------------------------------- + + // This marks the end of D3D11.1 opcodes + OPCODE_RESERVED_11_1, + + NUM_OPCODES, + OPCODE_INVAILD = NUM_OPCODES, +} OPCODE_TYPE; + +static OPCODE_TYPE DecodeOpcodeType(uint32_t ui32Token) +{ + return (OPCODE_TYPE)(ui32Token & 0x00007ff); +} + +typedef enum +{ + INDEX_0D, + INDEX_1D, + INDEX_2D, + INDEX_3D, +} OPERAND_INDEX_DIMENSION; + +static OPERAND_INDEX_DIMENSION DecodeOperandIndexDimension(uint32_t ui32Token) +{ + return (OPERAND_INDEX_DIMENSION)((ui32Token & 0x00300000) >> 20); +} + +typedef enum OPERAND_TYPE +{ + OPERAND_TYPE_SPECIAL_LOOPCOUNTER = -10, + OPERAND_TYPE_SPECIAL_IMMCONSTINT = -9, + OPERAND_TYPE_SPECIAL_TEXCOORD = -8, + OPERAND_TYPE_SPECIAL_POSITION = -7, + OPERAND_TYPE_SPECIAL_FOG = -6, + OPERAND_TYPE_SPECIAL_POINTSIZE = -5, + OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR = -4, + OPERAND_TYPE_SPECIAL_OUTBASECOLOUR = -3, + OPERAND_TYPE_SPECIAL_ADDRESS = -2, + OPERAND_TYPE_SPECIAL_IMMCONST = -1, + OPERAND_TYPE_TEMP = 0, // Temporary Register File + OPERAND_TYPE_INPUT = 1, // General Input Register File + OPERAND_TYPE_OUTPUT = 2, // General Output Register File + OPERAND_TYPE_INDEXABLE_TEMP = 3, // Temporary Register File (indexable) + OPERAND_TYPE_IMMEDIATE32 = 4, // 32bit/component immediate value(s) + // If for example, operand token bits + // [01:00]==OPERAND_4_COMPONENT, + // this means that the operand type: + // OPERAND_TYPE_IMMEDIATE32 + // results in 4 additional 32bit + // DWORDS present for the operand. + OPERAND_TYPE_IMMEDIATE64 = 5, // 64bit/comp.imm.val(s)HI:LO + OPERAND_TYPE_SAMPLER = 6, // Reference to sampler state + OPERAND_TYPE_RESOURCE = 7, // Reference to memory resource (e.g. texture) + OPERAND_TYPE_CONSTANT_BUFFER= 8, // Reference to constant buffer + OPERAND_TYPE_IMMEDIATE_CONSTANT_BUFFER= 9, // Reference to immediate constant buffer + OPERAND_TYPE_LABEL = 10, // Label + OPERAND_TYPE_INPUT_PRIMITIVEID = 11, // Input primitive ID + OPERAND_TYPE_OUTPUT_DEPTH = 12, // Output Depth + OPERAND_TYPE_NULL = 13, // Null register, used to discard results of operations + // Below Are operands new in DX 10.1 + OPERAND_TYPE_RASTERIZER = 14, // DX10.1 Rasterizer register, used to denote the depth/stencil and render target resources + OPERAND_TYPE_OUTPUT_COVERAGE_MASK = 15, // DX10.1 PS output MSAA coverage mask (scalar) + // Below Are operands new in DX 11 + OPERAND_TYPE_STREAM = 16, // Reference to GS stream output resource + OPERAND_TYPE_FUNCTION_BODY = 17, // Reference to a function definition + OPERAND_TYPE_FUNCTION_TABLE = 18, // Reference to a set of functions used by a class + OPERAND_TYPE_INTERFACE = 19, // Reference to an interface + OPERAND_TYPE_FUNCTION_INPUT = 20, // Reference to an input parameter to a function + OPERAND_TYPE_FUNCTION_OUTPUT = 21, // Reference to an output parameter to a function + OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID = 22, // HS Control Point phase input saying which output control point ID this is + OPERAND_TYPE_INPUT_FORK_INSTANCE_ID = 23, // HS Fork Phase input instance ID + OPERAND_TYPE_INPUT_JOIN_INSTANCE_ID = 24, // HS Join Phase input instance ID + OPERAND_TYPE_INPUT_CONTROL_POINT = 25, // HS Fork+Join, DS phase input control points (array of them) + OPERAND_TYPE_OUTPUT_CONTROL_POINT = 26, // HS Fork+Join phase output control points (array of them) + OPERAND_TYPE_INPUT_PATCH_CONSTANT = 27, // DS+HSJoin Input Patch Constants (array of them) + OPERAND_TYPE_INPUT_DOMAIN_POINT = 28, // DS Input Domain point + OPERAND_TYPE_THIS_POINTER = 29, // Reference to an interface this pointer + OPERAND_TYPE_UNORDERED_ACCESS_VIEW = 30, // Reference to UAV u# + OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY = 31, // Reference to Thread Group Shared Memory g# + OPERAND_TYPE_INPUT_THREAD_ID = 32, // Compute Shader Thread ID + OPERAND_TYPE_INPUT_THREAD_GROUP_ID = 33, // Compute Shader Thread Group ID + OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP = 34, // Compute Shader Thread ID In Thread Group + OPERAND_TYPE_INPUT_COVERAGE_MASK = 35, // Pixel shader coverage mask input + OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED = 36, // Compute Shader Thread ID In Group Flattened to a 1D value. + OPERAND_TYPE_INPUT_GS_INSTANCE_ID = 37, // Input GS instance ID + OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL = 38, // Output Depth, forced to be greater than or equal than current depth + OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL = 39, // Output Depth, forced to be less than or equal to current depth + OPERAND_TYPE_CYCLE_COUNTER = 40, // Cycle counter +} OPERAND_TYPE; + +static OPERAND_TYPE DecodeOperandType(uint32_t ui32Token) +{ + return (OPERAND_TYPE)((ui32Token & 0x000ff000) >> 12); +} + +static SPECIAL_NAME DecodeOperandSpecialName(uint32_t ui32Token) +{ + return (SPECIAL_NAME)(ui32Token & 0x0000ffff); +} + +typedef enum OPERAND_INDEX_REPRESENTATION +{ + OPERAND_INDEX_IMMEDIATE32 = 0, // Extra DWORD + OPERAND_INDEX_IMMEDIATE64 = 1, // 2 Extra DWORDs + // (HI32:LO32) + OPERAND_INDEX_RELATIVE = 2, // Extra operand + OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE = 3, // Extra DWORD followed by + // extra operand + OPERAND_INDEX_IMMEDIATE64_PLUS_RELATIVE = 4, // 2 Extra DWORDS + // (HI32:LO32) followed + // by extra operand +} OPERAND_INDEX_REPRESENTATION; + +static OPERAND_INDEX_REPRESENTATION DecodeOperandIndexRepresentation(uint32_t ui32Dimension, uint32_t ui32Token) +{ + return (OPERAND_INDEX_REPRESENTATION)((ui32Token & (0x3<<(22+3*((ui32Dimension)&3)))) >> (22+3*((ui32Dimension)&3))); +} + +typedef enum OPERAND_NUM_COMPONENTS +{ + OPERAND_0_COMPONENT = 0, + OPERAND_1_COMPONENT = 1, + OPERAND_4_COMPONENT = 2, + OPERAND_N_COMPONENT = 3 // unused for now +} OPERAND_NUM_COMPONENTS; + +static OPERAND_NUM_COMPONENTS DecodeOperandNumComponents(uint32_t ui32Token) +{ + return (OPERAND_NUM_COMPONENTS)(ui32Token & 0x00000003); +} + +typedef enum OPERAND_4_COMPONENT_SELECTION_MODE +{ + OPERAND_4_COMPONENT_MASK_MODE = 0, // mask 4 components + OPERAND_4_COMPONENT_SWIZZLE_MODE = 1, // swizzle 4 components + OPERAND_4_COMPONENT_SELECT_1_MODE = 2, // select 1 of 4 components +} OPERAND_4_COMPONENT_SELECTION_MODE; + +static OPERAND_4_COMPONENT_SELECTION_MODE DecodeOperand4CompSelMode(uint32_t ui32Token) +{ + return (OPERAND_4_COMPONENT_SELECTION_MODE)((ui32Token & 0x0000000c) >> 2); +} + +#define OPERAND_4_COMPONENT_MASK_X 0x00000001 +#define OPERAND_4_COMPONENT_MASK_Y 0x00000002 +#define OPERAND_4_COMPONENT_MASK_Z 0x00000004 +#define OPERAND_4_COMPONENT_MASK_W 0x00000008 +#define OPERAND_4_COMPONENT_MASK_R OPERAND_4_COMPONENT_MASK_X +#define OPERAND_4_COMPONENT_MASK_G OPERAND_4_COMPONENT_MASK_Y +#define OPERAND_4_COMPONENT_MASK_B OPERAND_4_COMPONENT_MASK_Z +#define OPERAND_4_COMPONENT_MASK_A OPERAND_4_COMPONENT_MASK_W +#define OPERAND_4_COMPONENT_MASK_ALL 0x0000000f + +static uint32_t DecodeOperand4CompMask(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x000000f0) >> 4); +} + +static uint32_t DecodeOperand4CompSwizzle(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x00000ff0) >> 4); +} + +static uint32_t DecodeOperand4CompSel1(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x00000030) >> 4); +} + +#define OPERAND_4_COMPONENT_X 0 +#define OPERAND_4_COMPONENT_Y 1 +#define OPERAND_4_COMPONENT_Z 2 +#define OPERAND_4_COMPONENT_W 3 + +static uint32_t NO_SWIZZLE = (( (OPERAND_4_COMPONENT_X) | (OPERAND_4_COMPONENT_Y<<2) | (OPERAND_4_COMPONENT_Z << 4) | (OPERAND_4_COMPONENT_W << 6))/*<<4*/); + +static uint32_t XXXX_SWIZZLE = (((OPERAND_4_COMPONENT_X) | (OPERAND_4_COMPONENT_X<<2) | (OPERAND_4_COMPONENT_X << 4) | (OPERAND_4_COMPONENT_X << 6))); +static uint32_t YYYY_SWIZZLE = (((OPERAND_4_COMPONENT_Y) | (OPERAND_4_COMPONENT_Y<<2) | (OPERAND_4_COMPONENT_Y << 4) | (OPERAND_4_COMPONENT_Y << 6))); +static uint32_t ZZZZ_SWIZZLE = (((OPERAND_4_COMPONENT_Z) | (OPERAND_4_COMPONENT_Z<<2) | (OPERAND_4_COMPONENT_Z << 4) | (OPERAND_4_COMPONENT_Z << 6))); +static uint32_t WWWW_SWIZZLE = (((OPERAND_4_COMPONENT_W) | (OPERAND_4_COMPONENT_W<<2) | (OPERAND_4_COMPONENT_W << 4) | (OPERAND_4_COMPONENT_W << 6))); + +static uint32_t DecodeOperand4CompSwizzleSource(uint32_t ui32Token, uint32_t comp) +{ + return (uint32_t)(((ui32Token)>>(4+2*((comp)&3)))&3); +} + +typedef enum RESOURCE_DIMENSION +{ + RESOURCE_DIMENSION_UNKNOWN = 0, + RESOURCE_DIMENSION_BUFFER = 1, + RESOURCE_DIMENSION_TEXTURE1D = 2, + RESOURCE_DIMENSION_TEXTURE2D = 3, + RESOURCE_DIMENSION_TEXTURE2DMS = 4, + RESOURCE_DIMENSION_TEXTURE3D = 5, + RESOURCE_DIMENSION_TEXTURECUBE = 6, + RESOURCE_DIMENSION_TEXTURE1DARRAY = 7, + RESOURCE_DIMENSION_TEXTURE2DARRAY = 8, + RESOURCE_DIMENSION_TEXTURE2DMSARRAY = 9, + RESOURCE_DIMENSION_TEXTURECUBEARRAY = 10, + RESOURCE_DIMENSION_RAW_BUFFER = 11, + RESOURCE_DIMENSION_STRUCTURED_BUFFER = 12, +} RESOURCE_DIMENSION; + +static RESOURCE_DIMENSION DecodeResourceDimension(uint32_t ui32Token) +{ + return (RESOURCE_DIMENSION)((ui32Token & 0x0000f800) >> 11); +} + +static RESOURCE_DIMENSION DecodeExtendedResourceDimension(uint32_t ui32Token) +{ + return (RESOURCE_DIMENSION)((ui32Token & 0x000007C0) >> 6); +} + +typedef enum CONSTANT_BUFFER_ACCESS_PATTERN +{ + CONSTANT_BUFFER_ACCESS_PATTERN_IMMEDIATEINDEXED = 0, + CONSTANT_BUFFER_ACCESS_PATTERN_DYNAMICINDEXED = 1 +} CONSTANT_BUFFER_ACCESS_PATTERN; + +static CONSTANT_BUFFER_ACCESS_PATTERN DecodeConstantBufferAccessPattern(uint32_t ui32Token) +{ + return (CONSTANT_BUFFER_ACCESS_PATTERN)((ui32Token & 0x00000800) >> 11); +} + +typedef enum INSTRUCTION_TEST_BOOLEAN +{ + INSTRUCTION_TEST_ZERO = 0, + INSTRUCTION_TEST_NONZERO = 1 +} INSTRUCTION_TEST_BOOLEAN; + +static INSTRUCTION_TEST_BOOLEAN DecodeInstrTestBool(uint32_t ui32Token) +{ + return (INSTRUCTION_TEST_BOOLEAN)((ui32Token & 0x00040000) >> 18); +} + +static uint32_t DecodeIsOperandExtended(uint32_t ui32Token) +{ + return (ui32Token & 0x80000000) >> 31; +} + +typedef enum EXTENDED_OPERAND_TYPE +{ + EXTENDED_OPERAND_EMPTY = 0, + EXTENDED_OPERAND_MODIFIER = 1, +} EXTENDED_OPERAND_TYPE; + +static EXTENDED_OPERAND_TYPE DecodeExtendedOperandType(uint32_t ui32Token) +{ + return (EXTENDED_OPERAND_TYPE)(ui32Token & 0x0000003f); +} + +typedef enum OPERAND_MODIFIER +{ + OPERAND_MODIFIER_NONE = 0, + OPERAND_MODIFIER_NEG = 1, + OPERAND_MODIFIER_ABS = 2, + OPERAND_MODIFIER_ABSNEG = 3, +} OPERAND_MODIFIER; + +static OPERAND_MODIFIER DecodeExtendedOperandModifier(uint32_t ui32Token) +{ + return (OPERAND_MODIFIER)((ui32Token & 0x00003fc0) >> 6); +} + +static const uint32_t GLOBAL_FLAG_REFACTORING_ALLOWED = (1<<11); +static const uint32_t GLOBAL_FLAG_ENABLE_DOUBLE_PRECISION_FLOAT_OPS = (1<<12); +static const uint32_t GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL = (1<<13); +static const uint32_t GLOBAL_FLAG_ENABLE_RAW_AND_STRUCTURED_BUFFERS = (1<<14); +static const uint32_t GLOBAL_FLAG_SKIP_OPTIMIZATION = (1<<15); +static const uint32_t GLOBAL_FLAG_ENABLE_MINIMUM_PRECISION = (1<<16); +static const uint32_t GLOBAL_FLAG_ENABLE_DOUBLE_EXTENSIONS = (1<<17); +static const uint32_t GLOBAL_FLAG_ENABLE_SHADER_EXTENSIONS = (1<<18); + +static uint32_t DecodeGlobalFlags(uint32_t ui32Token) +{ + return (uint32_t)(ui32Token & 0x00fff800); +} + +static INTERPOLATION_MODE DecodeInterpolationMode(uint32_t ui32Token) +{ + return (INTERPOLATION_MODE)((ui32Token & 0x00007800) >> 11); +} + + +typedef enum PRIMITIVE_TOPOLOGY +{ + PRIMITIVE_TOPOLOGY_UNDEFINED = 0, + PRIMITIVE_TOPOLOGY_POINTLIST = 1, + PRIMITIVE_TOPOLOGY_LINELIST = 2, + PRIMITIVE_TOPOLOGY_LINESTRIP = 3, + PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4, + PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5, + // 6 is reserved for legacy triangle fans + // Adjacency values should be equal to (0x8 & non-adjacency): + PRIMITIVE_TOPOLOGY_LINELIST_ADJ = 10, + PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ = 11, + PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ = 12, + PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ = 13, +} PRIMITIVE_TOPOLOGY; + +static PRIMITIVE_TOPOLOGY DecodeGSOutputPrimitiveTopology(uint32_t ui32Token) +{ + return (PRIMITIVE_TOPOLOGY)((ui32Token & 0x0001f800) >> 11); +} + +typedef enum PRIMITIVE +{ + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_POINT = 1, + PRIMITIVE_LINE = 2, + PRIMITIVE_TRIANGLE = 3, + // Adjacency values should be equal to (0x4 & non-adjacency): + PRIMITIVE_LINE_ADJ = 6, + PRIMITIVE_TRIANGLE_ADJ = 7, + PRIMITIVE_1_CONTROL_POINT_PATCH = 8, + PRIMITIVE_2_CONTROL_POINT_PATCH = 9, + PRIMITIVE_3_CONTROL_POINT_PATCH = 10, + PRIMITIVE_4_CONTROL_POINT_PATCH = 11, + PRIMITIVE_5_CONTROL_POINT_PATCH = 12, + PRIMITIVE_6_CONTROL_POINT_PATCH = 13, + PRIMITIVE_7_CONTROL_POINT_PATCH = 14, + PRIMITIVE_8_CONTROL_POINT_PATCH = 15, + PRIMITIVE_9_CONTROL_POINT_PATCH = 16, + PRIMITIVE_10_CONTROL_POINT_PATCH = 17, + PRIMITIVE_11_CONTROL_POINT_PATCH = 18, + PRIMITIVE_12_CONTROL_POINT_PATCH = 19, + PRIMITIVE_13_CONTROL_POINT_PATCH = 20, + PRIMITIVE_14_CONTROL_POINT_PATCH = 21, + PRIMITIVE_15_CONTROL_POINT_PATCH = 22, + PRIMITIVE_16_CONTROL_POINT_PATCH = 23, + PRIMITIVE_17_CONTROL_POINT_PATCH = 24, + PRIMITIVE_18_CONTROL_POINT_PATCH = 25, + PRIMITIVE_19_CONTROL_POINT_PATCH = 26, + PRIMITIVE_20_CONTROL_POINT_PATCH = 27, + PRIMITIVE_21_CONTROL_POINT_PATCH = 28, + PRIMITIVE_22_CONTROL_POINT_PATCH = 29, + PRIMITIVE_23_CONTROL_POINT_PATCH = 30, + PRIMITIVE_24_CONTROL_POINT_PATCH = 31, + PRIMITIVE_25_CONTROL_POINT_PATCH = 32, + PRIMITIVE_26_CONTROL_POINT_PATCH = 33, + PRIMITIVE_27_CONTROL_POINT_PATCH = 34, + PRIMITIVE_28_CONTROL_POINT_PATCH = 35, + PRIMITIVE_29_CONTROL_POINT_PATCH = 36, + PRIMITIVE_30_CONTROL_POINT_PATCH = 37, + PRIMITIVE_31_CONTROL_POINT_PATCH = 38, + PRIMITIVE_32_CONTROL_POINT_PATCH = 39, +} PRIMITIVE; + +static PRIMITIVE DecodeGSInputPrimitive(uint32_t ui32Token) +{ + return (PRIMITIVE)((ui32Token & 0x0001f800) >> 11); +} + +static TESSELLATOR_PARTITIONING DecodeTessPartitioning(uint32_t ui32Token) +{ + return (TESSELLATOR_PARTITIONING)((ui32Token & 0x00003800) >> 11); +} + +typedef enum TESSELLATOR_DOMAIN +{ + TESSELLATOR_DOMAIN_UNDEFINED = 0, + TESSELLATOR_DOMAIN_ISOLINE = 1, + TESSELLATOR_DOMAIN_TRI = 2, + TESSELLATOR_DOMAIN_QUAD = 3 +} TESSELLATOR_DOMAIN; + +static TESSELLATOR_DOMAIN DecodeTessDomain(uint32_t ui32Token) +{ + return (TESSELLATOR_DOMAIN)((ui32Token & 0x00001800) >> 11); +} + +static TESSELLATOR_OUTPUT_PRIMITIVE DecodeTessOutPrim(uint32_t ui32Token) +{ + return (TESSELLATOR_OUTPUT_PRIMITIVE)((ui32Token & 0x00003800) >> 11); +} + +static const uint32_t SYNC_THREADS_IN_GROUP = 0x00000800; +static const uint32_t SYNC_THREAD_GROUP_SHARED_MEMORY = 0x00001000; +static const uint32_t SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP = 0x00002000; +static const uint32_t SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GLOBAL = 0x00004000; + +static uint32_t DecodeSyncFlags(uint32_t ui32Token) +{ + return ui32Token & 0x00007800; +} + +// The number of types that implement this interface +static uint32_t DecodeInterfaceTableLength(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x0000ffff) >> 0); +} + +// The number of interfaces that are defined in this array. +static uint32_t DecodeInterfaceArrayLength(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0xffff0000) >> 16); +} + +typedef enum CUSTOMDATA_CLASS +{ + CUSTOMDATA_COMMENT = 0, + CUSTOMDATA_DEBUGINFO, + CUSTOMDATA_OPAQUE, + CUSTOMDATA_DCL_IMMEDIATE_CONSTANT_BUFFER, + CUSTOMDATA_SHADER_MESSAGE, +} CUSTOMDATA_CLASS; + +static CUSTOMDATA_CLASS DecodeCustomDataClass(uint32_t ui32Token) +{ + return (CUSTOMDATA_CLASS)((ui32Token & 0xfffff800) >> 11); +} + +static uint32_t DecodeInstructionSaturate(uint32_t ui32Token) +{ + return (ui32Token & 0x00002000) ? 1 : 0; +} + +typedef enum OPERAND_MIN_PRECISION +{ + OPERAND_MIN_PRECISION_DEFAULT = 0, // Default precision + // for the shader model + OPERAND_MIN_PRECISION_FLOAT_16 = 1, // Min 16 bit/component float + OPERAND_MIN_PRECISION_FLOAT_2_8 = 2, // Min 10(2.8)bit/comp. float + OPERAND_MIN_PRECISION_SINT_16 = 4, // Min 16 bit/comp. signed integer + OPERAND_MIN_PRECISION_UINT_16 = 5, // Min 16 bit/comp. unsigned integer +} OPERAND_MIN_PRECISION; + +static uint32_t DecodeOperandMinPrecision(uint32_t ui32Token) +{ + return (ui32Token & 0x0001C000) >> 14; +} + +static uint32_t DecodeOutputControlPointCount(uint32_t ui32Token) +{ + return ((ui32Token & 0x0001f800) >> 11); +} + +typedef enum IMMEDIATE_ADDRESS_OFFSET_COORD +{ + IMMEDIATE_ADDRESS_OFFSET_U = 0, + IMMEDIATE_ADDRESS_OFFSET_V = 1, + IMMEDIATE_ADDRESS_OFFSET_W = 2, +} IMMEDIATE_ADDRESS_OFFSET_COORD; + + +#define IMMEDIATE_ADDRESS_OFFSET_SHIFT(Coord) (9+4*((Coord)&3)) +#define IMMEDIATE_ADDRESS_OFFSET_MASK(Coord) (0x0000000f<>(IMMEDIATE_ADDRESS_OFFSET_SHIFT(eCoord)))); +} + +// UAV access scope flags +static const uint32_t GLOBALLY_COHERENT_ACCESS = 0x00010000; +static uint32_t DecodeAccessCoherencyFlags(uint32_t ui32Token) +{ + return ui32Token & 0x00010000; +} + + +typedef enum RESINFO_RETURN_TYPE +{ + RESINFO_INSTRUCTION_RETURN_FLOAT = 0, + RESINFO_INSTRUCTION_RETURN_RCPFLOAT = 1, + RESINFO_INSTRUCTION_RETURN_UINT = 2 +} RESINFO_RETURN_TYPE; + +static RESINFO_RETURN_TYPE DecodeResInfoReturnType(uint32_t ui32Token) +{ + return (RESINFO_RETURN_TYPE)((ui32Token & 0x00001800) >> 11); +} + +#include "tokensDX9.h" + +#endif diff --git a/Code/Tools/HLSLCrossCompiler/src/internal_includes/tokensDX9.h b/Code/Tools/HLSLCrossCompiler/src/internal_includes/tokensDX9.h new file mode 100644 index 0000000000..a71afd7b59 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/internal_includes/tokensDX9.h @@ -0,0 +1,304 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "debug.h" + +static const uint32_t D3D9SHADER_TYPE_VERTEX = 0xFFFE0000; +static const uint32_t D3D9SHADER_TYPE_PIXEL = 0xFFFF0000; + +static SHADER_TYPE DecodeShaderTypeDX9(const uint32_t ui32Token) +{ + uint32_t ui32Type = ui32Token & 0xFFFF0000; + if(ui32Type == D3D9SHADER_TYPE_VERTEX) + return VERTEX_SHADER; + + if(ui32Type == D3D9SHADER_TYPE_PIXEL) + return PIXEL_SHADER; + + return INVALID_SHADER; +} + +static uint32_t DecodeProgramMajorVersionDX9(const uint32_t ui32Token) +{ + return ((ui32Token)>>8)&0xFF; +} + +static uint32_t DecodeProgramMinorVersionDX9(const uint32_t ui32Token) +{ + return ui32Token & 0xFF; +} + +typedef enum +{ + OPCODE_DX9_NOP = 0, + OPCODE_DX9_MOV , + OPCODE_DX9_ADD , + OPCODE_DX9_SUB , + OPCODE_DX9_MAD , + OPCODE_DX9_MUL , + OPCODE_DX9_RCP , + OPCODE_DX9_RSQ , + OPCODE_DX9_DP3 , + OPCODE_DX9_DP4 , + OPCODE_DX9_MIN , + OPCODE_DX9_MAX , + OPCODE_DX9_SLT , + OPCODE_DX9_SGE , + OPCODE_DX9_EXP , + OPCODE_DX9_LOG , + OPCODE_DX9_LIT , + OPCODE_DX9_DST , + OPCODE_DX9_LRP , + OPCODE_DX9_FRC , + OPCODE_DX9_M4x4 , + OPCODE_DX9_M4x3 , + OPCODE_DX9_M3x4 , + OPCODE_DX9_M3x3 , + OPCODE_DX9_M3x2 , + OPCODE_DX9_CALL , + OPCODE_DX9_CALLNZ , + OPCODE_DX9_LOOP , + OPCODE_DX9_RET , + OPCODE_DX9_ENDLOOP , + OPCODE_DX9_LABEL , + OPCODE_DX9_DCL , + OPCODE_DX9_POW , + OPCODE_DX9_CRS , + OPCODE_DX9_SGN , + OPCODE_DX9_ABS , + OPCODE_DX9_NRM , + OPCODE_DX9_SINCOS , + OPCODE_DX9_REP , + OPCODE_DX9_ENDREP , + OPCODE_DX9_IF , + OPCODE_DX9_IFC , + OPCODE_DX9_ELSE , + OPCODE_DX9_ENDIF , + OPCODE_DX9_BREAK , + OPCODE_DX9_BREAKC , + OPCODE_DX9_MOVA , + OPCODE_DX9_DEFB , + OPCODE_DX9_DEFI , + + OPCODE_DX9_TEXCOORD = 64, + OPCODE_DX9_TEXKILL , + OPCODE_DX9_TEX , + OPCODE_DX9_TEXBEM , + OPCODE_DX9_TEXBEML , + OPCODE_DX9_TEXREG2AR , + OPCODE_DX9_TEXREG2GB , + OPCODE_DX9_TEXM3x2PAD , + OPCODE_DX9_TEXM3x2TEX , + OPCODE_DX9_TEXM3x3PAD , + OPCODE_DX9_TEXM3x3TEX , + OPCODE_DX9_RESERVED0 , + OPCODE_DX9_TEXM3x3SPEC , + OPCODE_DX9_TEXM3x3VSPEC , + OPCODE_DX9_EXPP , + OPCODE_DX9_LOGP , + OPCODE_DX9_CND , + OPCODE_DX9_DEF , + OPCODE_DX9_TEXREG2RGB , + OPCODE_DX9_TEXDP3TEX , + OPCODE_DX9_TEXM3x2DEPTH , + OPCODE_DX9_TEXDP3 , + OPCODE_DX9_TEXM3x3 , + OPCODE_DX9_TEXDEPTH , + OPCODE_DX9_CMP , + OPCODE_DX9_BEM , + OPCODE_DX9_DP2ADD , + OPCODE_DX9_DSX , + OPCODE_DX9_DSY , + OPCODE_DX9_TEXLDD , + OPCODE_DX9_SETP , + OPCODE_DX9_TEXLDL , + OPCODE_DX9_BREAKP , + + OPCODE_DX9_PHASE = 0xFFFD, + OPCODE_DX9_COMMENT = 0xFFFE, + OPCODE_DX9_END = 0xFFFF, + + OPCODE_DX9_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} OPCODE_TYPE_DX9; + +static OPCODE_TYPE_DX9 DecodeOpcodeTypeDX9(const uint32_t ui32Token) +{ + return (OPCODE_TYPE_DX9)(ui32Token & 0x0000FFFF); +} + +static uint32_t DecodeInstructionLengthDX9(const uint32_t ui32Token) +{ + return (ui32Token & 0x0F000000)>>24; +} + +static uint32_t DecodeCommentLengthDX9(const uint32_t ui32Token) +{ + return (ui32Token & 0x7FFF0000)>>16; +} + +static uint32_t DecodeOperandRegisterNumberDX9(const uint32_t ui32Token) +{ + return ui32Token & 0x000007FF; +} + +typedef enum +{ + OPERAND_TYPE_DX9_TEMP = 0, // Temporary Register File + OPERAND_TYPE_DX9_INPUT = 1, // Input Register File + OPERAND_TYPE_DX9_CONST = 2, // Constant Register File + OPERAND_TYPE_DX9_ADDR = 3, // Address Register (VS) + OPERAND_TYPE_DX9_TEXTURE = 3, // Texture Register File (PS) + OPERAND_TYPE_DX9_RASTOUT = 4, // Rasterizer Register File + OPERAND_TYPE_DX9_ATTROUT = 5, // Attribute Output Register File + OPERAND_TYPE_DX9_TEXCRDOUT = 6, // Texture Coordinate Output Register File + OPERAND_TYPE_DX9_OUTPUT = 6, // Output register file for VS3.0+ + OPERAND_TYPE_DX9_CONSTINT = 7, // Constant Integer Vector Register File + OPERAND_TYPE_DX9_COLOROUT = 8, // Color Output Register File + OPERAND_TYPE_DX9_DEPTHOUT = 9, // Depth Output Register File + OPERAND_TYPE_DX9_SAMPLER = 10, // Sampler State Register File + OPERAND_TYPE_DX9_CONST2 = 11, // Constant Register File 2048 - 4095 + OPERAND_TYPE_DX9_CONST3 = 12, // Constant Register File 4096 - 6143 + OPERAND_TYPE_DX9_CONST4 = 13, // Constant Register File 6144 - 8191 + OPERAND_TYPE_DX9_CONSTBOOL = 14, // Constant Boolean register file + OPERAND_TYPE_DX9_LOOP = 15, // Loop counter register file + OPERAND_TYPE_DX9_TEMPFLOAT16 = 16, // 16-bit float temp register file + OPERAND_TYPE_DX9_MISCTYPE = 17, // Miscellaneous (single) registers. + OPERAND_TYPE_DX9_LABEL = 18, // Label + OPERAND_TYPE_DX9_PREDICATE = 19, // Predicate register + OPERAND_TYPE_DX9_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} OPERAND_TYPE_DX9; + +static OPERAND_TYPE_DX9 DecodeOperandTypeDX9(const uint32_t ui32Token) +{ + return (OPERAND_TYPE_DX9)(((ui32Token & 0x70000000) >> 28) | + ((ui32Token & 0x00001800) >> 8)); +} + +static uint32_t CreateOperandTokenDX9(const uint32_t ui32RegNum, const OPERAND_TYPE_DX9 eType) +{ + uint32_t ui32Token = ui32RegNum; + ASSERT(ui32RegNum <2048); + ui32Token |= (eType <<28) & 0x70000000; + ui32Token |= (eType <<8) & 0x00001800; + return ui32Token; +} + +typedef enum { + DECLUSAGE_POSITION = 0, + DECLUSAGE_BLENDWEIGHT = 1, + DECLUSAGE_BLENDINDICES = 2, + DECLUSAGE_NORMAL = 3, + DECLUSAGE_PSIZE = 4, + DECLUSAGE_TEXCOORD = 5, + DECLUSAGE_TANGENT = 6, + DECLUSAGE_BINORMAL = 7, + DECLUSAGE_TESSFACTOR = 8, + DECLUSAGE_POSITIONT = 9, + DECLUSAGE_COLOR = 10, + DECLUSAGE_FOG = 11, + DECLUSAGE_DEPTH = 12, + DECLUSAGE_SAMPLE = 13 +} DECLUSAGE_DX9; + +static DECLUSAGE_DX9 DecodeUsageDX9(const uint32_t ui32Token) +{ + return (DECLUSAGE_DX9) (ui32Token & 0x0000000f); +} + +static uint32_t DecodeUsageIndexDX9(const uint32_t ui32Token) +{ + return (ui32Token & 0x000f0000)>>16; +} + +static uint32_t DecodeOperandIsRelativeAddressModeDX9(const uint32_t ui32Token) +{ + return ui32Token & (1<<13); +} + +static const uint32_t DX9_SWIZZLE_SHIFT = 16; +#define NO_SWIZZLE_DX9 ((0<>16); +} diff --git a/Code/Tools/HLSLCrossCompiler/src/reflect.c b/Code/Tools/HLSLCrossCompiler/src/reflect.c new file mode 100644 index 0000000000..66587c0152 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/reflect.c @@ -0,0 +1,1075 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/reflect.h" +#include "internal_includes/debug.h" +#include "internal_includes/decode.h" +#include "internal_includes/hlslcc_malloc.h" +#include "bstrlib.h" +#include +#include + +static void FormatVariableName(char* Name) +{ + int i; + + /* MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/bb944006(v=vs.85).aspx + The uniform function parameters appear in the + constant table prepended with a dollar sign ($), + unlike the global variables. The dollar sign is + required to avoid name collisions between local + uniform inputs and global variables of the same name.*/ + + /* Leave $ThisPointer, $Element and $Globals as-is. + Otherwise remove $ character ($ is not a valid character for GLSL variable names). */ + if(Name[0] == '$') + { + if(strcmp(Name, "$Element") !=0 && + strcmp(Name, "$Globals") != 0 && + strcmp(Name, "$ThisPointer") != 0) + { + Name[0] = '_'; + } + } + + // remove "__" because it's reserved in OpenGL + for (i = 0; Name[i] != '\0'; ++i) + { + if (Name[i] == '_' && Name[i + 1] == '_') + { + Name[i + 1] = 'x'; + } + } +} + +static void ReadStringFromTokenStream(const uint32_t* tokens, char* str) +{ + char* charTokens = (char*) tokens; + char nextCharacter = *charTokens++; + int length = 0; + + //Add each individual character until + //a terminator is found. + while(nextCharacter != 0) { + + str[length++] = nextCharacter; + + if(length > MAX_REFLECT_STRING_LENGTH) + { + str[length-1] = '\0'; + return; + } + + nextCharacter = *charTokens++; + } + + str[length] = '\0'; +} + +static void ReadInputSignatures(const uint32_t* pui32Tokens, ShaderInfo* psShaderInfo, const int extended) +{ + uint32_t i; + + InOutSignature* psSignatures; + const uint32_t* pui32FirstSignatureToken = pui32Tokens; + const uint32_t ui32ElementCount = *pui32Tokens++; + /* const uint32_t ui32Key = */ *pui32Tokens++; + + psSignatures = hlslcc_malloc(sizeof(InOutSignature) * ui32ElementCount); + psShaderInfo->psInputSignatures = psSignatures; + psShaderInfo->ui32NumInputSignatures = ui32ElementCount; + + for(i=0; iui32Stream = 0; + psCurrentSignature->eMinPrec = MIN_PRECISION_DEFAULT; + + if(extended) + psCurrentSignature->ui32Stream = *pui32Tokens++; + + ui32SemanticNameOffset = *pui32Tokens++; + psCurrentSignature->ui32SemanticIndex = *pui32Tokens++; + psCurrentSignature->eSystemValueType = (SPECIAL_NAME) *pui32Tokens++; + psCurrentSignature->eComponentType = (INOUT_COMPONENT_TYPE) *pui32Tokens++; + psCurrentSignature->ui32Register = *pui32Tokens++; + + ui32ComponentMasks = *pui32Tokens++; + psCurrentSignature->ui32Mask = ui32ComponentMasks & 0x7F; + //Shows which components are read + psCurrentSignature->ui32ReadWriteMask = (ui32ComponentMasks & 0x7F00) >> 8; + + if(extended) + psCurrentSignature->eMinPrec = *pui32Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstSignatureToken+ui32SemanticNameOffset), psCurrentSignature->SemanticName); + } +} + +static void ReadOutputSignatures(const uint32_t* pui32Tokens, ShaderInfo* psShaderInfo, const int minPrec, const int streams) +{ + uint32_t i; + + InOutSignature* psSignatures; + const uint32_t* pui32FirstSignatureToken = pui32Tokens; + const uint32_t ui32ElementCount = *pui32Tokens++; + /* const uint32_t ui32Key = */ *pui32Tokens++; + + psSignatures = hlslcc_malloc(sizeof(InOutSignature) * ui32ElementCount); + psShaderInfo->psOutputSignatures = psSignatures; + psShaderInfo->ui32NumOutputSignatures = ui32ElementCount; + + for(i=0; iui32Stream = 0; + psCurrentSignature->eMinPrec = MIN_PRECISION_DEFAULT; + + if(streams) + psCurrentSignature->ui32Stream = *pui32Tokens++; + + ui32SemanticNameOffset = *pui32Tokens++; + psCurrentSignature->ui32SemanticIndex = *pui32Tokens++; + psCurrentSignature->eSystemValueType = (SPECIAL_NAME)*pui32Tokens++; + psCurrentSignature->eComponentType = (INOUT_COMPONENT_TYPE) *pui32Tokens++; + psCurrentSignature->ui32Register = *pui32Tokens++; + + ui32ComponentMasks = *pui32Tokens++; + psCurrentSignature->ui32Mask = ui32ComponentMasks & 0x7F; + //Shows which components are NEVER written. + psCurrentSignature->ui32ReadWriteMask = (ui32ComponentMasks & 0x7F00) >> 8; + + if(minPrec) + psCurrentSignature->eMinPrec = *pui32Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstSignatureToken+ui32SemanticNameOffset), psCurrentSignature->SemanticName); + } +} + +static const uint32_t* ReadResourceBinding(const uint32_t* pui32FirstResourceToken, const uint32_t* pui32Tokens, ResourceBinding* psBinding) +{ + uint32_t ui32NameOffset = *pui32Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstResourceToken+ui32NameOffset), psBinding->Name); + FormatVariableName(psBinding->Name); + + psBinding->eType = *pui32Tokens++; + psBinding->ui32ReturnType = *pui32Tokens++; + psBinding->eDimension = (REFLECT_RESOURCE_DIMENSION)*pui32Tokens++; + psBinding->ui32NumSamples = *pui32Tokens++; + psBinding->ui32BindPoint = *pui32Tokens++; + psBinding->ui32BindCount = *pui32Tokens++; + psBinding->ui32Flags = *pui32Tokens++; + + return pui32Tokens; +} + +//Read D3D11_SHADER_TYPE_DESC +static void ReadShaderVariableType(const uint32_t ui32MajorVersion, const uint32_t* pui32FirstConstBufToken, const uint32_t* pui32tokens, ShaderVarType* varType) +{ + const uint16_t* pui16Tokens = (const uint16_t*) pui32tokens; + uint16_t ui32MemberCount; + uint32_t ui32MemberOffset; + const uint32_t* pui32MemberTokens; + uint32_t i; + + varType->Class = (SHADER_VARIABLE_CLASS)pui16Tokens[0]; + varType->Type = (SHADER_VARIABLE_TYPE)pui16Tokens[1]; + varType->Rows = pui16Tokens[2]; + varType->Columns = pui16Tokens[3]; + varType->Elements = pui16Tokens[4]; + + varType->MemberCount = ui32MemberCount = pui16Tokens[5]; + varType->Members = 0; + + if(ui32MemberCount) + { + varType->Members = (ShaderVarType*)hlslcc_malloc(sizeof(ShaderVarType)*ui32MemberCount); + + ui32MemberOffset = pui32tokens[3]; + + pui32MemberTokens = (const uint32_t*)((const char*)pui32FirstConstBufToken+ui32MemberOffset); + + for(i=0; i< ui32MemberCount; ++i) + { + uint32_t ui32NameOffset = *pui32MemberTokens++; + uint32_t ui32MemberTypeOffset = *pui32MemberTokens++; + + varType->Members[i].Parent = varType; + varType->Members[i].ParentCount = varType->ParentCount + 1; + + varType->Members[i].Offset = *pui32MemberTokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstConstBufToken+ui32NameOffset), varType->Members[i].Name); + + ReadShaderVariableType(ui32MajorVersion, pui32FirstConstBufToken, + (const uint32_t*)((const char*)pui32FirstConstBufToken+ui32MemberTypeOffset), &varType->Members[i]); + } + } +} + +static const uint32_t* ReadConstantBuffer(ShaderInfo* psShaderInfo, const uint32_t* pui32FirstConstBufToken, const uint32_t* pui32Tokens, ConstantBuffer* psBuffer) +{ + uint32_t i; + uint32_t ui32NameOffset = *pui32Tokens++; + uint32_t ui32VarCount = *pui32Tokens++; + uint32_t ui32VarOffset = *pui32Tokens++; + const uint32_t* pui32VarToken = (const uint32_t*)((const char*)pui32FirstConstBufToken+ui32VarOffset); + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstConstBufToken+ui32NameOffset), psBuffer->Name); + FormatVariableName(psBuffer->Name); + + psBuffer->ui32NumVars = ui32VarCount; + + for(i=0; iasVars[i]; + + uint32_t ui32TypeOffset; + uint32_t ui32DefaultValueOffset; + + ui32NameOffset = *pui32VarToken++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstConstBufToken+ui32NameOffset), psVar->Name); + FormatVariableName(psVar->Name); + + psVar->ui32StartOffset = *pui32VarToken++; + psVar->ui32Size = *pui32VarToken++; + psVar->ui32Flags = *pui32VarToken++; + ui32TypeOffset = *pui32VarToken++; + + strcpy(psVar->sType.Name, psVar->Name); + psVar->sType.Parent = 0; + psVar->sType.ParentCount = 0; + psVar->sType.Offset = 0; + + ReadShaderVariableType(psShaderInfo->ui32MajorVersion, pui32FirstConstBufToken, + (const uint32_t*)((const char*)pui32FirstConstBufToken+ui32TypeOffset), &psVar->sType); + + ui32DefaultValueOffset = *pui32VarToken++; + + + if (psShaderInfo->ui32MajorVersion >= 5) + { + /* uint32_t StartTexture = */ *pui32VarToken++; + /* uint32_t TextureSize = */ *pui32VarToken++; + /* uint32_t StartSampler = */ *pui32VarToken++; + /* uint32_t SamplerSize = */ *pui32VarToken++; + } + + psVar->haveDefaultValue = 0; + + if(ui32DefaultValueOffset) + { + const uint32_t ui32NumDefaultValues = psVar->ui32Size / 4; + const uint32_t* pui32DefaultValToken = (const uint32_t*)((const char*)pui32FirstConstBufToken+ui32DefaultValueOffset); + + //Always a sequence of 4-bytes at the moment. + //bool const becomes 0 or 0xFFFFFFFF int, int & float are 4-bytes. + ASSERT(psVar->ui32Size%4 == 0); + + psVar->haveDefaultValue = 1; + + psVar->pui32DefaultValues = hlslcc_malloc(psVar->ui32Size); + + for(uint32_t j=0; jpui32DefaultValues[j] = pui32DefaultValToken[j]; + } + } + } + + + { + uint32_t ui32Flags; + uint32_t ui32BufferType; + + psBuffer->ui32TotalSizeInBytes = *pui32Tokens++; + psBuffer->blob = 0; + ui32Flags = *pui32Tokens++; + ui32BufferType = *pui32Tokens++; + } + + return pui32Tokens; +} + +static void ReadResources(const uint32_t* pui32Tokens, ShaderInfo* psShaderInfo) +{ + ResourceBinding* psResBindings; + ConstantBuffer* psConstantBuffers; + const uint32_t* pui32ConstantBuffers; + const uint32_t* pui32ResourceBindings; + const uint32_t* pui32FirstToken = pui32Tokens; + uint32_t i; + + const uint32_t ui32NumConstantBuffers = *pui32Tokens++; + const uint32_t ui32ConstantBufferOffset = *pui32Tokens++; + + uint32_t ui32NumResourceBindings = *pui32Tokens++; + uint32_t ui32ResourceBindingOffset = *pui32Tokens++; + /* uint32_t ui32ShaderModel = */ *pui32Tokens++; + /* uint32_t ui32CompileFlags = */ *pui32Tokens++;//D3DCompile flags? http://msdn.microsoft.com/en-us/library/gg615083(v=vs.85).aspx + + //Resources + pui32ResourceBindings = (const uint32_t*)((const char*)pui32FirstToken + ui32ResourceBindingOffset); + + psResBindings = hlslcc_malloc(sizeof(ResourceBinding)*ui32NumResourceBindings); + + psShaderInfo->ui32NumResourceBindings = ui32NumResourceBindings; + psShaderInfo->psResourceBindings = psResBindings; + + for(i=0; i < ui32NumResourceBindings; ++i) + { + pui32ResourceBindings = ReadResourceBinding(pui32FirstToken, pui32ResourceBindings, psResBindings+i); + ASSERT(psResBindings[i].ui32BindPoint < MAX_RESOURCE_BINDINGS); + } + + //Constant buffers + pui32ConstantBuffers = (const uint32_t*)((const char*)pui32FirstToken + ui32ConstantBufferOffset); + + psConstantBuffers = hlslcc_malloc(sizeof(ConstantBuffer) * ui32NumConstantBuffers); + + psShaderInfo->ui32NumConstantBuffers = ui32NumConstantBuffers; + psShaderInfo->psConstantBuffers = psConstantBuffers; + + for(i=0; i < ui32NumConstantBuffers; ++i) + { + pui32ConstantBuffers = ReadConstantBuffer(psShaderInfo, pui32FirstToken, pui32ConstantBuffers, psConstantBuffers+i); + } + + + //Map resource bindings to constant buffers + if(psShaderInfo->ui32NumConstantBuffers) + { + for(i=0; i < ui32NumResourceBindings; ++i) + { + ResourceGroup eRGroup; + uint32_t cbufIndex = 0; + + eRGroup = ResourceTypeToResourceGroup(psResBindings[i].eType); + + //Find the constant buffer whose name matches the resource at the given resource binding point + for(cbufIndex=0; cbufIndex < psShaderInfo->ui32NumConstantBuffers; cbufIndex++) + { + if(strcmp(psConstantBuffers[cbufIndex].Name, psResBindings[i].Name) == 0) + { + psShaderInfo->aui32ResourceMap[eRGroup][psResBindings[i].ui32BindPoint] = cbufIndex; + } + } + } + } +} + +static const uint16_t* ReadClassType(const uint32_t* pui32FirstInterfaceToken, const uint16_t* pui16Tokens, ClassType* psClassType) +{ + const uint32_t* pui32Tokens = (const uint32_t*)pui16Tokens; + uint32_t ui32NameOffset = *pui32Tokens; + pui16Tokens+= 2; + + psClassType->ui16ID = *pui16Tokens++; + psClassType->ui16ConstBufStride = *pui16Tokens++; + psClassType->ui16Texture = *pui16Tokens++; + psClassType->ui16Sampler = *pui16Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstInterfaceToken+ui32NameOffset), psClassType->Name); + + return pui16Tokens; +} + +static const uint16_t* ReadClassInstance(const uint32_t* pui32FirstInterfaceToken, const uint16_t* pui16Tokens, ClassInstance* psClassInstance) +{ + uint32_t ui32NameOffset = *pui16Tokens++ << 16; + ui32NameOffset |= *pui16Tokens++; + + psClassInstance->ui16ID = *pui16Tokens++; + psClassInstance->ui16ConstBuf = *pui16Tokens++; + psClassInstance->ui16ConstBufOffset = *pui16Tokens++; + psClassInstance->ui16Texture = *pui16Tokens++; + psClassInstance->ui16Sampler = *pui16Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstInterfaceToken+ui32NameOffset), psClassInstance->Name); + + return pui16Tokens; +} + + +static void ReadInterfaces(const uint32_t* pui32Tokens, ShaderInfo* psShaderInfo) +{ + uint32_t i; + uint32_t ui32StartSlot; + const uint32_t* pui32FirstInterfaceToken = pui32Tokens; + const uint32_t ui32ClassInstanceCount = *pui32Tokens++; + const uint32_t ui32ClassTypeCount = *pui32Tokens++; + const uint32_t ui32InterfaceSlotRecordCount = *pui32Tokens++; + /* const uint32_t ui32InterfaceSlotCount = */ *pui32Tokens++; + const uint32_t ui32ClassInstanceOffset = *pui32Tokens++; + const uint32_t ui32ClassTypeOffset = *pui32Tokens++; + const uint32_t ui32InterfaceSlotOffset = *pui32Tokens++; + + const uint16_t* pui16ClassTypes = (const uint16_t*)((const char*)pui32FirstInterfaceToken + ui32ClassTypeOffset); + const uint16_t* pui16ClassInstances = (const uint16_t*)((const char*)pui32FirstInterfaceToken + ui32ClassInstanceOffset); + const uint32_t* pui32InterfaceSlots = (const uint32_t*)((const char*)pui32FirstInterfaceToken + ui32InterfaceSlotOffset); + + const uint32_t* pui32InterfaceSlotTokens = pui32InterfaceSlots; + + ClassType* psClassTypes; + ClassInstance* psClassInstances; + + psClassTypes = hlslcc_malloc(sizeof(ClassType) * ui32ClassTypeCount); + for(i=0; iaui32TableIDToTypeID[*pui32TableID++] = *pui16TypeID++; + } + + ui32StartSlot += ui32SlotSpan; + } + + psShaderInfo->ui32NumClassInstances = ui32ClassInstanceCount; + psShaderInfo->psClassInstances = psClassInstances; + + psShaderInfo->ui32NumClassTypes = ui32ClassTypeCount; + psShaderInfo->psClassTypes = psClassTypes; +} + +void GetConstantBufferFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ShaderInfo* psShaderInfo, ConstantBuffer** ppsConstBuf) +{ + if(psShaderInfo->ui32MajorVersion > 3) + { + *ppsConstBuf = psShaderInfo->psConstantBuffers + psShaderInfo->aui32ResourceMap[eGroup][ui32BindPoint]; + } + else + { + ASSERT(psShaderInfo->ui32NumConstantBuffers == 1); + *ppsConstBuf = psShaderInfo->psConstantBuffers; + } +} + +int GetResourceFromBindingPoint(const ResourceGroup eGroup, uint32_t const ui32BindPoint, const ShaderInfo* psShaderInfo, ResourceBinding** ppsOutBinding) +{ + uint32_t i; + const uint32_t ui32NumBindings = psShaderInfo->ui32NumResourceBindings; + ResourceBinding* psBindings = psShaderInfo->psResourceBindings; + + for(i=0; i= psBindings[i].ui32BindPoint && ui32BindPoint < (psBindings[i].ui32BindPoint + psBindings[i].ui32BindCount)) + { + *ppsOutBinding = psBindings + i; + return 1; + } + } + } + return 0; +} + +int GetInterfaceVarFromOffset(uint32_t ui32Offset, ShaderInfo* psShaderInfo, ShaderVar** ppsShaderVar) +{ + uint32_t i; + ConstantBuffer* psThisPointerConstBuffer = psShaderInfo->psThisPointerConstBuffer; + + const uint32_t ui32NumVars = psThisPointerConstBuffer->ui32NumVars; + + for(i=0; i= psThisPointerConstBuffer->asVars[i].ui32StartOffset && + ui32Offset < (psThisPointerConstBuffer->asVars[i].ui32StartOffset + psThisPointerConstBuffer->asVars[i].ui32Size)) + { + *ppsShaderVar = &psThisPointerConstBuffer->asVars[i]; + return 1; + } + } + return 0; +} + +int GetInputSignatureFromRegister(const uint32_t ui32Register, const ShaderInfo* psShaderInfo, InOutSignature** ppsOut) +{ + uint32_t i; + const uint32_t ui32NumVars = psShaderInfo->ui32NumInputSignatures; + + for(i=0; ipsInputSignatures; + if(ui32Register == psInputSignatures[i].ui32Register) + { + *ppsOut = psInputSignatures+i; + return 1; + } + } + return 0; +} + +int GetOutputSignatureFromRegister(const uint32_t ui32Register, const uint32_t ui32CompMask, const uint32_t ui32Stream, ShaderInfo* psShaderInfo, InOutSignature** ppsOut) +{ + uint32_t i; + const uint32_t ui32NumVars = psShaderInfo->ui32NumOutputSignatures; + + for(i=0; ipsOutputSignatures; + if(ui32Register == psOutputSignatures[i].ui32Register && + (ui32CompMask & psOutputSignatures[i].ui32Mask) && + ui32Stream == psOutputSignatures[i].ui32Stream) + { + *ppsOut = psOutputSignatures+i; + return 1; + } + } + return 0; +} + +int GetOutputSignatureFromSystemValue(SPECIAL_NAME eSystemValueType, uint32_t ui32SemanticIndex, ShaderInfo* psShaderInfo, InOutSignature** ppsOut) +{ + uint32_t i; + const uint32_t ui32NumVars = psShaderInfo->ui32NumOutputSignatures; + + for(i=0; ipsOutputSignatures; + if(eSystemValueType == psOutputSignatures[i].eSystemValueType && + ui32SemanticIndex == psOutputSignatures[i].ui32SemanticIndex) + { + *ppsOut = psOutputSignatures+i; + return 1; + } + } + return 0; +} + +static int IsOffsetInType(ShaderVarType* psType, uint32_t parentOffset, uint32_t offsetToFind, const uint32_t* pui32Swizzle, int32_t* pi32Index, int32_t* pi32Rebase) +{ + uint32_t thisOffset = parentOffset + psType->Offset; + uint32_t thisSize = psType->Columns * psType->Rows * 4; + + if(psType->Elements) + { + thisSize += 16 * (psType->Elements - 1); + } + + //Swizzle can point to another variable. In the example below + //cbUIUpdates.g_uMaxFaces would be cb1[2].z. The scalars are combined + //into vectors. psCBuf->ui32NumVars will be 3. + + // cbuffer cbUIUpdates + // { + // + // float g_fLifeSpan; // Offset: 0 Size: 4 + // float g_fLifeSpanVar; // Offset: 4 Size: 4 [unused] + // float g_fRadiusMin; // Offset: 8 Size: 4 [unused] + // float g_fRadiusMax; // Offset: 12 Size: 4 [unused] + // float g_fGrowTime; // Offset: 16 Size: 4 [unused] + // float g_fStepSize; // Offset: 20 Size: 4 + // float g_fTurnRate; // Offset: 24 Size: 4 + // float g_fTurnSpeed; // Offset: 28 Size: 4 [unused] + // float g_fLeafRate; // Offset: 32 Size: 4 + // float g_fShrinkTime; // Offset: 36 Size: 4 [unused] + // uint g_uMaxFaces; // Offset: 40 Size: 4 + // + // } + + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cbUIUpdates cbuffer NA NA 1 1 + + if(pui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + offsetToFind += 4; + } + else + if(pui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + offsetToFind += 8; + } + else + if(pui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + offsetToFind += 12; + } + + if((offsetToFind >= thisOffset) && + offsetToFind < (thisOffset + thisSize)) + { + + if(psType->Class == SVC_MATRIX_ROWS || + psType->Class == SVC_MATRIX_COLUMNS) + { + //Matrices are treated as arrays of vectors. + pi32Index[0] = (offsetToFind - thisOffset) / 16; + } + //Check for array of vectors + else if(psType->Class == SVC_VECTOR && psType->Elements > 1) + { + pi32Index[0] = (offsetToFind - thisOffset) / 16; + } + else if(psType->Class == SVC_VECTOR && psType->Columns > 1) + { + //Check for vector starting at a non-vec4 offset. + + // cbuffer $Globals + // { + // + // float angle; // Offset: 0 Size: 4 + // float2 angle2; // Offset: 4 Size: 8 + // + // } + + //cb0[0].x = angle + //cb0[0].yzyy = angle2.xyxx + + //Rebase angle2 so that .y maps to .x, .z maps to .y + + pi32Rebase[0] = thisOffset % 16; + } + + return 1; + } + return 0; +} + +int GetShaderVarFromOffset(const uint32_t ui32Vec4Offset, const uint32_t* pui32Swizzle, ConstantBuffer* psCBuf, ShaderVarType** ppsShaderVar, int32_t* pi32Index, int32_t* pi32Rebase) +{ + uint32_t i; + + uint32_t ui32ByteOffset = ui32Vec4Offset * 16; + + const uint32_t ui32NumVars = psCBuf->ui32NumVars; + + for(i=0; iasVars[i].sType.Class == SVC_STRUCT) + { + uint32_t m = 0; + + for(m=0; m < psCBuf->asVars[i].sType.MemberCount; ++m) + { + ShaderVarType* psMember = psCBuf->asVars[i].sType.Members + m; + + ASSERT(psMember->Class != SVC_STRUCT); + + if(IsOffsetInType(psMember, psCBuf->asVars[i].ui32StartOffset, ui32ByteOffset, pui32Swizzle, pi32Index, pi32Rebase)) + { + ppsShaderVar[0] = psMember; + return 1; + } + } + } + else + { + if(IsOffsetInType(&psCBuf->asVars[i].sType, psCBuf->asVars[i].ui32StartOffset, ui32ByteOffset, pui32Swizzle, pi32Index, pi32Rebase)) + { + ppsShaderVar[0] = &psCBuf->asVars[i].sType; + return 1; + } + } + } + return 0; +} + +ResourceGroup ResourceTypeToResourceGroup(ResourceType eType) +{ + switch(eType) + { + case RTYPE_CBUFFER: + return RGROUP_CBUFFER; + + case RTYPE_SAMPLER: + return RGROUP_SAMPLER; + + case RTYPE_TEXTURE: + case RTYPE_BYTEADDRESS: + case RTYPE_STRUCTURED: + return RGROUP_TEXTURE; + + case RTYPE_UAV_RWTYPED: + case RTYPE_UAV_RWSTRUCTURED: + case RTYPE_UAV_RWBYTEADDRESS: + case RTYPE_UAV_APPEND_STRUCTURED: + case RTYPE_UAV_CONSUME_STRUCTURED: + case RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER: + return RGROUP_UAV; + + case RTYPE_TBUFFER: + ASSERT(0); // Need to find out which group this belongs to + return RGROUP_TEXTURE; + } + + ASSERT(0); + return RGROUP_CBUFFER; +} + +void LoadShaderInfo(const uint32_t ui32MajorVersion, const uint32_t ui32MinorVersion, const ReflectionChunks* psChunks, ShaderInfo* psInfo) +{ + uint32_t i; + const uint32_t* pui32Inputs = psChunks->pui32Inputs; + const uint32_t* pui32Inputs11 = psChunks->pui32Inputs11; + const uint32_t* pui32Resources = psChunks->pui32Resources; + const uint32_t* pui32Interfaces = psChunks->pui32Interfaces; + const uint32_t* pui32Outputs = psChunks->pui32Outputs; + const uint32_t* pui32Outputs11 = psChunks->pui32Outputs11; + const uint32_t* pui32OutputsWithStreams = psChunks->pui32OutputsWithStreams; + + psInfo->eTessOutPrim = TESSELLATOR_OUTPUT_UNDEFINED; + psInfo->eTessPartitioning = TESSELLATOR_PARTITIONING_UNDEFINED; + for(i=0; iaePixelInputInterpolation[i] = INTERPOLATION_LINEAR; + + psInfo->ui32MajorVersion = ui32MajorVersion; + psInfo->ui32MinorVersion = ui32MinorVersion; + + psInfo->ui32NumImports = 0; + psInfo->ui32NumExports = 0; + psInfo->psImports = 0; + psInfo->psExports = 0; + psInfo->ui32InputHash = 0; + psInfo->ui32SymbolsOffset = 0; + psInfo->ui32NumSamplers = 0; + + if(pui32Inputs) + ReadInputSignatures(pui32Inputs, psInfo, 0); + if(pui32Inputs11) + ReadInputSignatures(pui32Inputs11, psInfo, 1); + if(pui32Resources) + ReadResources(pui32Resources, psInfo); + if(pui32Interfaces) + ReadInterfaces(pui32Interfaces, psInfo); + if(pui32Outputs) + ReadOutputSignatures(pui32Outputs, psInfo, 0, 0); + if(pui32Outputs11) + ReadOutputSignatures(pui32Outputs11, psInfo, 1, 1); + if(pui32OutputsWithStreams) + ReadOutputSignatures(pui32OutputsWithStreams, psInfo, 0, 1); + + for(i=0; iui32NumConstantBuffers;++i) + { + bstring cbufName = bfromcstr(&psInfo->psConstantBuffers[i].Name[0]); + bstring cbufThisPointer = bfromcstr("$ThisPointer"); + if(bstrcmp(cbufName, cbufThisPointer) == 0) + { + psInfo->psThisPointerConstBuffer = &psInfo->psConstantBuffers[i]; + } + bdestroy(cbufName); + bdestroy(cbufThisPointer); + } + + memset(psInfo->asSamplers, 0, sizeof(psInfo->asSamplers)); +} + +void FreeShaderInfo(ShaderInfo* psShaderInfo) +{ + uint32_t uStep; + //Free any default values for constants. + uint32_t cbuf; + for(cbuf=0; cbufui32NumConstantBuffers; ++cbuf) + { + ConstantBuffer* psCBuf = &psShaderInfo->psConstantBuffers[cbuf]; + uint32_t var; + for(var=0; var < psCBuf->ui32NumVars; ++var) + { + ShaderVar* psVar = &psCBuf->asVars[var]; + hlslcc_free(psVar->sType.Members); + if(psVar->haveDefaultValue) + { + hlslcc_free(psVar->pui32DefaultValues); + } + } + } + hlslcc_free(psShaderInfo->psInputSignatures); + hlslcc_free(psShaderInfo->psResourceBindings); + hlslcc_free(psShaderInfo->psConstantBuffers); + hlslcc_free(psShaderInfo->psClassTypes); + hlslcc_free(psShaderInfo->psClassInstances); + hlslcc_free(psShaderInfo->psOutputSignatures); + hlslcc_free(psShaderInfo->psImports); + hlslcc_free(psShaderInfo->psExports); + + for (uStep = 0; uStep < psShaderInfo->ui32NumTraceSteps; ++uStep) + { + hlslcc_free(psShaderInfo->psTraceSteps[uStep].psVariables); + } + hlslcc_free(psShaderInfo->psTraceSteps); + + psShaderInfo->ui32NumInputSignatures = 0; + psShaderInfo->ui32NumResourceBindings = 0; + psShaderInfo->ui32NumConstantBuffers = 0; + psShaderInfo->ui32NumClassTypes = 0; + psShaderInfo->ui32NumClassInstances = 0; + psShaderInfo->ui32NumOutputSignatures = 0; + psShaderInfo->ui32NumTraceSteps = 0; + psShaderInfo->ui32NumImports = 0; + psShaderInfo->ui32NumExports = 0; +} + +typedef struct ConstantTableD3D9_TAG +{ + uint32_t size; + uint32_t creator; + uint32_t version; + uint32_t constants; + uint32_t constantInfos; + uint32_t flags; + uint32_t target; +} ConstantTableD3D9; + +// These enums match those in d3dx9shader.h. +enum RegisterSet +{ + RS_BOOL, + RS_INT4, + RS_FLOAT4, + RS_SAMPLER, +}; + +enum TypeClass +{ + CLASS_SCALAR, + CLASS_VECTOR, + CLASS_MATRIX_ROWS, + CLASS_MATRIX_COLUMNS, + CLASS_OBJECT, + CLASS_STRUCT, +}; + +enum Type +{ + PT_VOID, + PT_BOOL, + PT_INT, + PT_FLOAT, + PT_STRING, + PT_TEXTURE, + PT_TEXTURE1D, + PT_TEXTURE2D, + PT_TEXTURE3D, + PT_TEXTURECUBE, + PT_SAMPLER, + PT_SAMPLER1D, + PT_SAMPLER2D, + PT_SAMPLER3D, + PT_SAMPLERCUBE, + PT_PIXELSHADER, + PT_VERTEXSHADER, + PT_PIXELFRAGMENT, + PT_VERTEXFRAGMENT, + PT_UNSUPPORTED, +}; +typedef struct ConstantInfoD3D9_TAG +{ + uint32_t name; + uint16_t registerSet; + uint16_t registerIndex; + uint16_t registerCount; + uint16_t reserved; + uint32_t typeInfo; + uint32_t defaultValue; +} ConstantInfoD3D9; + +typedef struct TypeInfoD3D9_TAG +{ + uint16_t typeClass; + uint16_t type; + uint16_t rows; + uint16_t columns; + uint16_t elements; + uint16_t structMembers; + uint32_t structMemberInfos; +} TypeInfoD3D9; + +typedef struct StructMemberInfoD3D9_TAG +{ + uint32_t name; + uint32_t typeInfo; +} StructMemberInfoD3D9; + +void LoadD3D9ConstantTable(const char* data, ShaderInfo* psInfo) +{ + ConstantTableD3D9* ctab; + uint32_t constNum; + ConstantInfoD3D9* cinfos; + ConstantBuffer* psConstantBuffer; + uint32_t ui32ConstantBufferSize = 0; + uint32_t numResourceBindingsNeeded = 0; + ShaderVar* var; + + ctab = (ConstantTableD3D9*)data; + + cinfos = (ConstantInfoD3D9*)(data + ctab->constantInfos); + + psInfo->ui32NumConstantBuffers++; + + //Only 1 Constant Table in d3d9 + ASSERT(psInfo->ui32NumConstantBuffers == 1); + + psConstantBuffer = hlslcc_malloc(sizeof(ConstantBuffer)); + + psInfo->psConstantBuffers = psConstantBuffer; + + psConstantBuffer->ui32NumVars = 0; + strcpy(psConstantBuffer->Name, "$Globals"); + + //Determine how many resource bindings to create + for (constNum = 0; constNum < ctab->constants; ++constNum) + { + if (cinfos[constNum].registerSet == RS_SAMPLER) + { + ++numResourceBindingsNeeded; + } + } + + psInfo->psResourceBindings = hlslcc_malloc(numResourceBindingsNeeded * sizeof(ResourceBinding)); + + var = &psConstantBuffer->asVars[0]; + + for (constNum = 0; constNum < ctab->constants; ++constNum) + { + TypeInfoD3D9* typeInfo = (TypeInfoD3D9*)(data + cinfos[constNum].typeInfo); + + if (cinfos[constNum].registerSet != RS_SAMPLER) + { + strcpy(var->Name, data + cinfos[constNum].name); + FormatVariableName(var->Name); + var->ui32Size = cinfos[constNum].registerCount * 16; + var->ui32StartOffset = cinfos[constNum].registerIndex * 16; + var->haveDefaultValue = 0; + + if (ui32ConstantBufferSize < (var->ui32Size + var->ui32StartOffset)) + { + ui32ConstantBufferSize = var->ui32Size + var->ui32StartOffset; + } + + var->sType.Rows = typeInfo->rows; + var->sType.Columns = typeInfo->columns; + var->sType.Elements = typeInfo->elements; + var->sType.MemberCount = typeInfo->structMembers; + var->sType.Members = 0; + var->sType.Offset = 0; + var->sType.Parent = 0; + var->sType.ParentCount = 0; + + switch (typeInfo->typeClass) + { + case CLASS_SCALAR: + { + var->sType.Class = SVC_SCALAR; + break; + } + case CLASS_VECTOR: + { + var->sType.Class = SVC_VECTOR; + break; + } + case CLASS_MATRIX_ROWS: + { + var->sType.Class = SVC_MATRIX_ROWS; + break; + } + case CLASS_MATRIX_COLUMNS: + { + var->sType.Class = SVC_MATRIX_COLUMNS; + break; + } + case CLASS_OBJECT: + { + var->sType.Class = SVC_OBJECT; + break; + } + case CLASS_STRUCT: + { + var->sType.Class = SVC_STRUCT; + break; + } + } + + switch (cinfos[constNum].registerSet) + { + case RS_BOOL: + { + var->sType.Type = SVT_BOOL; + break; + } + case RS_INT4: + { + var->sType.Type = SVT_INT; + break; + } + case RS_FLOAT4: + { + var->sType.Type = SVT_FLOAT; + break; + } + } + + var++; + psConstantBuffer->ui32NumVars++; + } + else + { + //Create a resource if it is sampler in order to replicate the d3d10+ + //method of separating samplers from general constants. + uint32_t ui32ResourceIndex = psInfo->ui32NumResourceBindings++; + ResourceBinding* res = &psInfo->psResourceBindings[ui32ResourceIndex]; + + strcpy(res->Name, data + cinfos[constNum].name); + FormatVariableName(res->Name); + + res->ui32BindPoint = cinfos[constNum].registerIndex; + res->ui32BindCount = cinfos[constNum].registerCount; + res->ui32Flags = 0; + res->ui32NumSamples = 1; + res->ui32ReturnType = 0; + + res->eType = RTYPE_TEXTURE; + + switch (typeInfo->type) + { + case PT_SAMPLER: + case PT_SAMPLER1D: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURE1D; + break; + case PT_SAMPLER2D: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURE2D; + break; + case PT_SAMPLER3D: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURE2D; + break; + case PT_SAMPLERCUBE: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURECUBE; + break; + } + } + } + psConstantBuffer->ui32TotalSizeInBytes = ui32ConstantBufferSize; +} diff --git a/Code/Tools/HLSLCrossCompiler/src/toGLSL.c b/Code/Tools/HLSLCrossCompiler/src/toGLSL.c new file mode 100644 index 0000000000..ff1546b703 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/toGLSL.c @@ -0,0 +1,1921 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/tokens.h" +#include "internal_includes/structs.h" +#include "internal_includes/decode.h" +#include "stdlib.h" +#include "stdio.h" +#include "bstrlib.h" +#include "internal_includes/toGLSLInstruction.h" +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/toGLSLDeclaration.h" +#include "internal_includes/languages.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/hlslccToolkit.h" +#include "../offline/hash.h" + +#if defined(_WIN32) && !defined(PORTABLE) +#include +AZ_PUSH_DISABLE_WARNING(4115, "-Wunknown-warning-option") // 4115: named type definition in parentheses +#include +AZ_POP_DISABLE_WARNING +#pragma comment(lib,"d3dcompiler.lib") +#endif //defined(_WIN32) && !defined(PORTABLE) + +#ifndef GL_VERTEX_SHADER_ARB +#define GL_VERTEX_SHADER_ARB 0x8B31 +#endif +#ifndef GL_FRAGMENT_SHADER_ARB +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#endif +#ifndef GL_GEOMETRY_SHADER +#define GL_GEOMETRY_SHADER 0x8DD9 +#endif +#ifndef GL_TESS_EVALUATION_SHADER +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#endif +#ifndef GL_TESS_CONTROL_SHADER +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif +#ifndef GL_COMPUTE_SHADER +#define GL_COMPUTE_SHADER 0x91B9 +#endif + + +HLSLCC_API void HLSLCC_APIENTRY HLSLcc_SetMemoryFunctions(void* (*malloc_override)(size_t), void* (*calloc_override)(size_t, size_t), void (* free_override)(void*), void* (*realloc_override)(void*, size_t)) +{ + hlslcc_malloc = malloc_override; + hlslcc_calloc = calloc_override; + hlslcc_free = free_override; + hlslcc_realloc = realloc_override; +} + +void AddIndentation(HLSLCrossCompilerContext* psContext) +{ + int i; + int indent = psContext->indent; + bstring glsl = *psContext->currentGLSLString; + for (i = 0; i < indent; ++i) + { + bcatcstr(glsl, " "); + } +} + +uint32_t AddImport(HLSLCrossCompilerContext* psContext, SYMBOL_TYPE eType, uint32_t ui32ID, uint32_t ui32Default) +{ + bstring glsl = *psContext->currentGLSLString; + uint32_t ui32Symbol = psContext->psShader->sInfo.ui32NumImports; + + psContext->psShader->sInfo.psImports = (Symbol*)hlslcc_realloc(psContext->psShader->sInfo.psImports, (ui32Symbol + 1) * sizeof(Symbol)); + ++psContext->psShader->sInfo.ui32NumImports; + + bformata(glsl, "#ifndef IMPORT_%d\n", ui32Symbol); + bformata(glsl, "#define IMPORT_%d %d\n", ui32Symbol, ui32Default); + bformata(glsl, "#endif\n", ui32Symbol); + + psContext->psShader->sInfo.psImports[ui32Symbol].eType = eType; + psContext->psShader->sInfo.psImports[ui32Symbol].ui32ID = ui32ID; + psContext->psShader->sInfo.psImports[ui32Symbol].ui32Value = ui32Default; + + return ui32Symbol; +} + +uint32_t AddExport(HLSLCrossCompilerContext* psContext, SYMBOL_TYPE eType, uint32_t ui32ID, uint32_t ui32Value) +{ + uint32_t ui32Param = psContext->psShader->sInfo.ui32NumExports; + + psContext->psShader->sInfo.psExports = (Symbol*)hlslcc_realloc(psContext->psShader->sInfo.psExports, (ui32Param + 1) * sizeof(Symbol)); + ++psContext->psShader->sInfo.ui32NumExports; + + psContext->psShader->sInfo.psExports[ui32Param].eType = eType; + psContext->psShader->sInfo.psExports[ui32Param].ui32ID = ui32ID; + psContext->psShader->sInfo.psExports[ui32Param].ui32Value = ui32Value; + + return ui32Param; +} + +void AddVersionDependentCode(HLSLCrossCompilerContext* psContext) +{ + bstring glsl = *psContext->currentGLSLString; + uint32_t ui32DepthClampImp; + + if (!HaveCompute(psContext->psShader->eTargetLanguage)) + { + if (psContext->psShader->eShaderType == COMPUTE_SHADER) + { + bcatcstr(glsl, "#extension GL_ARB_compute_shader : enable\n"); + bcatcstr(glsl, "#extension GL_ARB_shader_storage_buffer_object : enable\n"); + } + } + + if (!HaveAtomicMem(psContext->psShader->eTargetLanguage) || + !HaveAtomicCounter(psContext->psShader->eTargetLanguage)) + { + if (psContext->psShader->aiOpcodeUsed[OPCODE_IMM_ATOMIC_ALLOC] || + psContext->psShader->aiOpcodeUsed[OPCODE_IMM_ATOMIC_CONSUME] || + psContext->psShader->aiOpcodeUsed[OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED]) + { + bcatcstr(glsl, "#extension GL_ARB_shader_atomic_counters : enable\n"); + + bcatcstr(glsl, "#extension GL_ARB_shader_storage_buffer_object : enable\n"); + } + } + + if (!HaveGather(psContext->psShader->eTargetLanguage)) + { + if (psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO_C] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_C]) + { + bcatcstr(glsl, "#extension GL_ARB_texture_gather : enable\n"); + } + } + + if (!HaveGatherNonConstOffset(psContext->psShader->eTargetLanguage)) + { + if (psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO_C] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO]) + { + bcatcstr(glsl, "#extension GL_ARB_gpu_shader5 : enable\n"); + } + } + + if (!HaveQueryLod(psContext->psShader->eTargetLanguage)) + { + if (psContext->psShader->aiOpcodeUsed[OPCODE_LOD]) + { + bcatcstr(glsl, "#extension GL_ARB_texture_query_lod : enable\n"); + } + } + + if (!HaveQueryLevels(psContext->psShader->eTargetLanguage)) + { + if (psContext->psShader->aiOpcodeUsed[OPCODE_RESINFO]) + { + bcatcstr(glsl, "#extension GL_ARB_texture_query_levels : enable\n"); + } + } + + if (!HaveImageLoadStore(psContext->psShader->eTargetLanguage) && (psContext->flags & HLSLCC_FLAG_AVOID_SHADER_LOAD_STORE_EXTENSION) == 0) + { + if (psContext->psShader->aiOpcodeUsed[OPCODE_STORE_UAV_TYPED] || + psContext->psShader->aiOpcodeUsed[OPCODE_STORE_RAW] || + psContext->psShader->aiOpcodeUsed[OPCODE_STORE_STRUCTURED]) + { + bcatcstr(glsl, "#extension GL_ARB_shader_image_load_store : enable\n"); + bcatcstr(glsl, "#extension GL_ARB_shader_bit_encoding : enable\n"); + } + else + if (psContext->psShader->aiOpcodeUsed[OPCODE_LD_UAV_TYPED] || + psContext->psShader->aiOpcodeUsed[OPCODE_LD_RAW] || + psContext->psShader->aiOpcodeUsed[OPCODE_LD_STRUCTURED]) + { + bcatcstr(glsl, "#extension GL_ARB_shader_image_load_store : enable\n"); + } + } + + + // #extension directive must occur before any non-preprocessor token + if (EmulateDepthClamp(psContext->psShader->eTargetLanguage) && (psContext->psShader->eShaderType == VERTEX_SHADER || psContext->psShader->eShaderType == PIXEL_SHADER)) + { + ui32DepthClampImp = AddImport(psContext, SYMBOL_EMULATE_DEPTH_CLAMP, 0, 0); + + bformata(glsl, "#if IMPORT_%d > 0\n", ui32DepthClampImp); + if (!HaveNoperspectiveInterpolation(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "#ifdef GL_NV_shader_noperspective_interpolation\n"); + bcatcstr(glsl, "#extension GL_NV_shader_noperspective_interpolation:enable\n"); + bformata(glsl, "#endif\n"); + } + bformata(glsl, "#endif\n"); + } + + if (psContext->psShader->ui32MajorVersion <= 3) + { + bcatcstr(glsl, "int RepCounter;\n"); + bcatcstr(glsl, "int LoopCounter;\n"); + bcatcstr(glsl, "int ZeroBasedCounter;\n"); + if (psContext->psShader->eShaderType == VERTEX_SHADER) + { + uint32_t texCoord; + bcatcstr(glsl, "ivec4 Address;\n"); + + if (InOutSupported(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "out vec4 OffsetColour;\n"); + bcatcstr(glsl, "out vec4 BaseColour;\n"); + + bcatcstr(glsl, "out vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "out vec4 TexCoord%d;\n", texCoord); + } + } + else + { + bcatcstr(glsl, "varying vec4 OffsetColour;\n"); + bcatcstr(glsl, "varying vec4 BaseColour;\n"); + + bcatcstr(glsl, "varying vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "varying vec4 TexCoord%d;\n", texCoord); + } + } + } + else + { + uint32_t renderTargets, texCoord; + + bcatcstr(glsl, "varying vec4 OffsetColour;\n"); + bcatcstr(glsl, "varying vec4 BaseColour;\n"); + + bcatcstr(glsl, "varying vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "varying vec4 TexCoord%d;\n", texCoord); + } + + for (renderTargets = 0; renderTargets < 8; ++renderTargets) + { + bformata(glsl, "#define Output%d gl_FragData[%d]\n", renderTargets, renderTargets); + } + } + } + + + if ((psContext->flags & HLSLCC_FLAG_ORIGIN_UPPER_LEFT) + && (psContext->psShader->eTargetLanguage >= LANG_150) + && (psContext->psShader->eShaderType == PIXEL_SHADER)) + { + bcatcstr(glsl, "layout(origin_upper_left) in vec4 gl_FragCoord;\n"); + } + + if ((psContext->flags & HLSLCC_FLAG_PIXEL_CENTER_INTEGER) + && (psContext->psShader->eTargetLanguage >= LANG_150)) + { + bcatcstr(glsl, "layout(pixel_center_integer) in vec4 gl_FragCoord;\n"); + } + + /* For versions which do not support a vec1 (currently all versions) */ + bcatcstr(glsl, "struct vec1 {\n"); + if (psContext->psShader->eTargetLanguage == LANG_ES_300 || psContext->psShader->eTargetLanguage == LANG_ES_310 || psContext->psShader->eTargetLanguage == LANG_ES_100) + { + bcatcstr(glsl, "\thighp float x;\n"); + } + else + { + bcatcstr(glsl, "\tfloat x;\n"); + } + bcatcstr(glsl, "};\n"); + + if (HaveUVec(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "struct uvec1 {\n"); + bcatcstr(glsl, "\tuint x;\n"); + bcatcstr(glsl, "};\n"); + } + + bcatcstr(glsl, "struct ivec1 {\n"); + bcatcstr(glsl, "\tint x;\n"); + bcatcstr(glsl, "};\n"); + + /* + OpenGL 4.1 API spec: + To use any built-in input or output in the gl_PerVertex block in separable + program objects, shader code must redeclare that block prior to use. + */ + if (psContext->psShader->eShaderType == VERTEX_SHADER && psContext->psShader->eTargetLanguage >= LANG_410) + { + bcatcstr(glsl, "out gl_PerVertex {\n"); + bcatcstr(glsl, "vec4 gl_Position;\n"); + bcatcstr(glsl, "float gl_PointSize;\n"); + bcatcstr(glsl, "float gl_ClipDistance[];"); + bcatcstr(glsl, "};\n"); + } + + //The fragment language has no default precision qualifier for floating point types. + if (psContext->psShader->eShaderType == PIXEL_SHADER && + psContext->psShader->eTargetLanguage == LANG_ES_100 || psContext->psShader->eTargetLanguage == LANG_ES_300 || psContext->psShader->eTargetLanguage == LANG_ES_310) + { + bcatcstr(glsl, "precision highp float;\n"); + } + + /* There is no default precision qualifier for the following sampler types in either the vertex or fragment language: */ + if (psContext->psShader->eTargetLanguage == LANG_ES_300 || psContext->psShader->eTargetLanguage == LANG_ES_310) + { + bcatcstr(glsl, "precision lowp sampler3D;\n"); + bcatcstr(glsl, "precision lowp samplerCubeShadow;\n"); + bcatcstr(glsl, "precision lowp sampler2DShadow;\n"); + bcatcstr(glsl, "precision lowp sampler2DArray;\n"); + bcatcstr(glsl, "precision lowp sampler2DArrayShadow;\n"); + bcatcstr(glsl, "precision lowp isampler2D;\n"); + bcatcstr(glsl, "precision lowp isampler3D;\n"); + bcatcstr(glsl, "precision lowp isamplerCube;\n"); + bcatcstr(glsl, "precision lowp isampler2DArray;\n"); + bcatcstr(glsl, "precision lowp usampler2D;\n"); + bcatcstr(glsl, "precision lowp usampler3D;\n"); + bcatcstr(glsl, "precision lowp usamplerCube;\n"); + bcatcstr(glsl, "precision lowp usampler2DArray;\n"); + + if (psContext->psShader->eTargetLanguage == LANG_ES_310) + { + bcatcstr(glsl, "precision lowp isampler2DMS;\n"); + bcatcstr(glsl, "precision lowp usampler2D;\n"); + bcatcstr(glsl, "precision lowp usampler3D;\n"); + bcatcstr(glsl, "precision lowp usamplerCube;\n"); + bcatcstr(glsl, "precision lowp usampler2DArray;\n"); + bcatcstr(glsl, "precision lowp usampler2DMS;\n"); + bcatcstr(glsl, "precision lowp image2D;\n"); + bcatcstr(glsl, "precision lowp image3D;\n"); + bcatcstr(glsl, "precision lowp imageCube;\n"); + bcatcstr(glsl, "precision lowp image2DArray;\n"); + bcatcstr(glsl, "precision lowp iimage2D;\n"); + bcatcstr(glsl, "precision lowp iimage3D;\n"); + bcatcstr(glsl, "precision lowp iimageCube;\n"); + bcatcstr(glsl, "precision lowp uimage2DArray;\n"); + //Only highp is valid for atomic_uint + bcatcstr(glsl, "precision highp atomic_uint;\n"); + } + } + + if (SubroutinesSupported(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "subroutine void SubroutineType();\n"); + } + + if (EmulateDepthClamp(psContext->psShader->eTargetLanguage) && (psContext->psShader->eShaderType == VERTEX_SHADER || psContext->psShader->eShaderType == PIXEL_SHADER)) + { + char* szInOut = psContext->psShader->eShaderType == VERTEX_SHADER ? "out" : "in"; + + bformata(glsl, "#if IMPORT_%d > 0\n", ui32DepthClampImp); + if (!HaveNoperspectiveInterpolation(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "#ifdef GL_NV_shader_noperspective_interpolation\n"); + } + bcatcstr(glsl, "#define EMULATE_DEPTH_CLAMP 1\n"); + bformata(glsl, "noperspective %s float unclampedDepth;\n", szInOut); + if (!HaveNoperspectiveInterpolation(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "#else\n"); + bcatcstr(glsl, "#define EMULATE_DEPTH_CLAMP 2\n"); + bformata(glsl, "%s float unclampedZ;\n", szInOut); + bformata(glsl, "#endif\n"); + } + bformata(glsl, "#endif\n"); + + if (psContext->psShader->eShaderType == PIXEL_SHADER) + { + bcatcstr(psContext->earlyMain, "#ifdef EMULATE_DEPTH_CLAMP\n"); + bcatcstr(psContext->earlyMain, "#if EMULATE_DEPTH_CLAMP == 2\n"); + bcatcstr(psContext->earlyMain, "\tfloat unclampedDepth = gl_DepthRange.near + unclampedZ * gl_FragCoord.w;\n"); + bcatcstr(psContext->earlyMain, "#endif\n"); + bcatcstr(psContext->earlyMain, "\tgl_FragDepth = clamp(unclampedDepth, 0.0, 1.0);\n"); + bcatcstr(psContext->earlyMain, "#endif\n"); + } + } +} + +FRAMEBUFFER_FETCH_TYPE CollectGmemInfo(HLSLCrossCompilerContext* psContext) +{ + FRAMEBUFFER_FETCH_TYPE fetchType = FBF_NONE; + Shader* psShader = psContext->psShader; + memset(psContext->rendertargetUse, 0x00, sizeof(psContext->rendertargetUse)); + for (uint32_t i = 0; i < psShader->ui32DeclCount; ++i) + { + Declaration* decl = psShader->psDecl + i; + if (decl->eOpcode == OPCODE_DCL_RESOURCE) + { + if (IsGmemReservedSlot(FBF_EXT_COLOR, decl->asOperands[0].ui32RegisterNumber)) + { + int regNum = GetGmemInputResourceSlot(decl->asOperands[0].ui32RegisterNumber); + ASSERT(regNum < MAX_COLOR_MRT); + psContext->rendertargetUse[regNum] |= INPUT_RENDERTARGET; + fetchType |= FBF_EXT_COLOR; + } + else if (IsGmemReservedSlot(FBF_ARM_COLOR, decl->asOperands[0].ui32RegisterNumber)) + { + fetchType |= FBF_ARM_COLOR; + } + else if (IsGmemReservedSlot(FBF_ARM_DEPTH, decl->asOperands[0].ui32RegisterNumber)) + { + fetchType |= FBF_ARM_DEPTH; + } + else if (IsGmemReservedSlot(FBF_ARM_STENCIL, decl->asOperands[0].ui32RegisterNumber)) + { + fetchType |= FBF_ARM_STENCIL; + } + } + else if (decl->eOpcode == OPCODE_DCL_OUTPUT && psShader->eShaderType == PIXEL_SHADER && decl->asOperands[0].eType != OPERAND_TYPE_OUTPUT_DEPTH) + { + ASSERT(decl->asOperands[0].ui32RegisterNumber < MAX_COLOR_MRT); + psContext->rendertargetUse[decl->asOperands[0].ui32RegisterNumber] |= OUTPUT_RENDERTARGET; + } + } + + return fetchType; +} + +uint16_t GetOpcodeWriteMask(OPCODE_TYPE eOpcode) +{ + switch (eOpcode) + { + default: + ASSERT(0); + + // No writes + case OPCODE_ENDREP: + case OPCODE_REP: + case OPCODE_BREAK: + case OPCODE_BREAKC: + case OPCODE_CALL: + case OPCODE_CALLC: + case OPCODE_CASE: + case OPCODE_CONTINUE: + case OPCODE_CONTINUEC: + case OPCODE_CUT: + case OPCODE_DISCARD: + case OPCODE_ELSE: + case OPCODE_EMIT: + case OPCODE_EMITTHENCUT: + case OPCODE_ENDIF: + case OPCODE_ENDLOOP: + case OPCODE_ENDSWITCH: + case OPCODE_IF: + case OPCODE_LABEL: + case OPCODE_LOOP: + case OPCODE_NOP: + case OPCODE_RET: + case OPCODE_RETC: + case OPCODE_SWITCH: + case OPCODE_HS_DECLS: + case OPCODE_HS_CONTROL_POINT_PHASE: + case OPCODE_HS_FORK_PHASE: + case OPCODE_HS_JOIN_PHASE: + case OPCODE_EMIT_STREAM: + case OPCODE_CUT_STREAM: + case OPCODE_EMITTHENCUT_STREAM: + case OPCODE_INTERFACE_CALL: + case OPCODE_STORE_UAV_TYPED: + case OPCODE_STORE_RAW: + case OPCODE_STORE_STRUCTURED: + case OPCODE_ATOMIC_AND: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_IMAX: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_UMIN: + case OPCODE_SYNC: + case OPCODE_ABORT: + case OPCODE_DEBUG_BREAK: + return 0; + + // Write to 0 + case OPCODE_POW: + case OPCODE_DP2ADD: + case OPCODE_LRP: + case OPCODE_ADD: + case OPCODE_AND: + case OPCODE_DERIV_RTX: + case OPCODE_DERIV_RTY: + case OPCODE_DEFAULT: + case OPCODE_DIV: + case OPCODE_DP2: + case OPCODE_DP3: + case OPCODE_DP4: + case OPCODE_EXP: + case OPCODE_FRC: + case OPCODE_ITOF: + case OPCODE_LOG: + case OPCODE_LT: + case OPCODE_MAD: + case OPCODE_MIN: + case OPCODE_MAX: + case OPCODE_MUL: + case OPCODE_ROUND_NE: + case OPCODE_ROUND_NI: + case OPCODE_ROUND_PI: + case OPCODE_ROUND_Z: + case OPCODE_RSQ: + case OPCODE_SQRT: + case OPCODE_UTOF: + case OPCODE_SAMPLE_POS: + case OPCODE_SAMPLE_INFO: + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_RCP: + case OPCODE_F32TOF16: + case OPCODE_F16TOF32: + case OPCODE_DTOF: + case OPCODE_EQ: + case OPCODE_FTOU: + case OPCODE_GE: + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_NE: + case OPCODE_NOT: + case OPCODE_OR: + case OPCODE_ULT: + case OPCODE_UGE: + case OPCODE_UMAD: + case OPCODE_XOR: + case OPCODE_UMAX: + case OPCODE_UMIN: + case OPCODE_USHR: + case OPCODE_COUNTBITS: + case OPCODE_FIRSTBIT_HI: + case OPCODE_FIRSTBIT_LO: + case OPCODE_FIRSTBIT_SHI: + case OPCODE_UBFE: + case OPCODE_BFI: + case OPCODE_BFREV: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_MSAD: + case OPCODE_DTOU: + case OPCODE_FTOI: + case OPCODE_IADD: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INE: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_BUFINFO: + case OPCODE_IBFE: + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_DTOI: + case OPCODE_DADD: + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DMOV: + case OPCODE_DMOVC: + case OPCODE_FTOD: + case OPCODE_DDIV: + case OPCODE_DFMA: + case OPCODE_DRCP: + case OPCODE_ITOD: + case OPCODE_UTOD: + case OPCODE_LD: + case OPCODE_LD_MS: + case OPCODE_RESINFO: + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_D: + case OPCODE_SAMPLE_B: + case OPCODE_LOD: + case OPCODE_GATHER4: + case OPCODE_GATHER4_C: + case OPCODE_GATHER4_PO: + case OPCODE_GATHER4_PO_C: + case OPCODE_LD_UAV_TYPED: + case OPCODE_LD_RAW: + case OPCODE_LD_STRUCTURED: + case OPCODE_EVAL_SNAPPED: + case OPCODE_EVAL_SAMPLE_INDEX: + case OPCODE_EVAL_CENTROID: + case OPCODE_MOV: + case OPCODE_MOVC: + return 1u << 0; + + // Write to 0, 1 + case OPCODE_SINCOS: + case OPCODE_UDIV: + case OPCODE_UMUL: + case OPCODE_UADDC: + case OPCODE_USUBB: + case OPCODE_SWAPC: + return (1u << 0) | (1u << 1); + } +} + +void CreateTracingInfo(Shader* psShader) +{ + VariableTraceInfo asInputVarsInfo[MAX_SHADER_VEC4_INPUT * 4]; + uint32_t ui32NumInputVars = 0; + uint32_t uInputVec, uInstruction; + + psShader->sInfo.ui32NumTraceSteps = psShader->ui32InstCount + 1; + psShader->sInfo.psTraceSteps = hlslcc_malloc(sizeof(StepTraceInfo) * psShader->sInfo.ui32NumTraceSteps); + + for (uInputVec = 0; uInputVec < psShader->sInfo.ui32NumInputSignatures; ++uInputVec) + { + uint32_t ui32RWMask = psShader->sInfo.psInputSignatures[uInputVec].ui32ReadWriteMask; + uint8_t ui8Component = 0; + + while (ui32RWMask != 0) + { + if (ui32RWMask & 1) + { + TRACE_VARIABLE_TYPE eType; + switch (psShader->sInfo.psInputSignatures[uInputVec].eComponentType) + { + default: + ASSERT(0); + case INOUT_COMPONENT_UNKNOWN: + case INOUT_COMPONENT_UINT32: + eType = TRACE_VARIABLE_UINT; + break; + case INOUT_COMPONENT_SINT32: + eType = TRACE_VARIABLE_SINT; + break; + case INOUT_COMPONENT_FLOAT32: + eType = TRACE_VARIABLE_FLOAT; + break; + } + + asInputVarsInfo[ui32NumInputVars].eGroup = TRACE_VARIABLE_INPUT; + asInputVarsInfo[ui32NumInputVars].eType = eType; + asInputVarsInfo[ui32NumInputVars].ui8Index = psShader->sInfo.psInputSignatures[uInputVec].ui32Register; + asInputVarsInfo[ui32NumInputVars].ui8Component = ui8Component; + ++ui32NumInputVars; + } + ui32RWMask >>= 1; + ++ui8Component; + } + } + + psShader->sInfo.psTraceSteps[0].ui32NumVariables = ui32NumInputVars; + psShader->sInfo.psTraceSteps[0].psVariables = hlslcc_malloc(sizeof(VariableTraceInfo) * ui32NumInputVars); + memcpy(psShader->sInfo.psTraceSteps[0].psVariables, asInputVarsInfo, sizeof(VariableTraceInfo) * ui32NumInputVars); + + for (uInstruction = 0; uInstruction < psShader->ui32InstCount; ++uInstruction) + { + VariableTraceInfo* psStepVars = NULL; + uint32_t ui32StepVarsCapacity = 0; + uint32_t ui32StepVarsSize = 0; + uint32_t auStepDirtyVecMask[MAX_TEMP_VEC4 + MAX_SHADER_VEC4_OUTPUT] = {0}; + uint8_t auStepCompTypeMask[4 * (MAX_TEMP_VEC4 + MAX_SHADER_VEC4_OUTPUT)] = {0}; + uint32_t uOpcodeWriteMask = GetOpcodeWriteMask(psShader->psInst[uInstruction].eOpcode); + uint32_t uOperand, uStepVec; + + for (uOperand = 0; uOperand < psShader->psInst[uInstruction].ui32NumOperands; ++uOperand) + { + if (uOpcodeWriteMask & (1 << uOperand)) + { + uint32_t ui32OperandCompMask = ConvertOperandSwizzleToComponentMask(&psShader->psInst[uInstruction].asOperands[uOperand]); + uint32_t ui32Register = psShader->psInst[uInstruction].asOperands[uOperand].ui32RegisterNumber; + uint32_t ui32VecOffset = 0; + uint8_t ui8Component = 0; + switch (psShader->psInst[uInstruction].asOperands[uOperand].eType) + { + case OPERAND_TYPE_TEMP: + ui32VecOffset = 0; + break; + case OPERAND_TYPE_OUTPUT: + ui32VecOffset = MAX_TEMP_VEC4; + break; + default: + continue; + } + + auStepDirtyVecMask[ui32VecOffset + ui32Register] |= ui32OperandCompMask; + while (ui32OperandCompMask) + { + ASSERT(ui8Component < 4); + if (ui32OperandCompMask & 1) + { + TRACE_VARIABLE_TYPE eOperandCompType = TRACE_VARIABLE_UNKNOWN; + switch (psShader->psInst[uInstruction].asOperands[uOperand].aeDataType[ui8Component]) + { + case SVT_INT: + eOperandCompType = TRACE_VARIABLE_SINT; + break; + case SVT_FLOAT: + eOperandCompType = TRACE_VARIABLE_FLOAT; + break; + case SVT_UINT: + eOperandCompType = TRACE_VARIABLE_UINT; + break; + case SVT_DOUBLE: + eOperandCompType = TRACE_VARIABLE_DOUBLE; + break; + } + if (auStepCompTypeMask[4 * (ui32VecOffset + ui32Register) + ui8Component] == 0) + { + auStepCompTypeMask[4 * (ui32VecOffset + ui32Register) + ui8Component] = 1u + (uint8_t)eOperandCompType; + } + else if (auStepCompTypeMask[4 * (ui32VecOffset + ui32Register) + ui8Component] != eOperandCompType) + { + auStepCompTypeMask[4 * (ui32VecOffset + ui32Register) + ui8Component] = 1u + (uint8_t)TRACE_VARIABLE_UNKNOWN; + } + } + ui32OperandCompMask >>= 1; + ++ui8Component; + } + } + } + + for (uStepVec = 0; uStepVec < MAX_TEMP_VEC4 + MAX_SHADER_VEC4_OUTPUT; ++uStepVec) + { + TRACE_VARIABLE_GROUP eGroup; + uint32_t uBase; + uint8_t ui8Component = 0; + if (uStepVec < MAX_TEMP_VEC4) + { + eGroup = TRACE_VARIABLE_TEMP; + uBase = 0; + } + else + { + eGroup = TRACE_VARIABLE_OUTPUT; + uBase = MAX_TEMP_VEC4; + } + + while (auStepDirtyVecMask[uStepVec] != 0) + { + if (auStepDirtyVecMask[uStepVec] & 1) + { + if (ui32StepVarsCapacity == ui32StepVarsSize) + { + ui32StepVarsCapacity = (1 > ui32StepVarsCapacity ? 1 : ui32StepVarsCapacity) * 16; + if (psStepVars == NULL) + { + psStepVars = hlslcc_malloc(ui32StepVarsCapacity * sizeof(VariableTraceInfo)); + } + else + { + psStepVars = hlslcc_realloc(psStepVars, ui32StepVarsCapacity * sizeof(VariableTraceInfo)); + } + } + ASSERT(ui32StepVarsSize < ui32StepVarsCapacity); + + psStepVars[ui32StepVarsSize].eGroup = eGroup; + psStepVars[ui32StepVarsSize].eType = auStepCompTypeMask[4 * uStepVec + ui8Component] == 0 ? TRACE_VARIABLE_UNKNOWN : (TRACE_VARIABLE_TYPE)(auStepCompTypeMask[4 * uStepVec + ui8Component] - 1); + psStepVars[ui32StepVarsSize].ui8Component = ui8Component; + psStepVars[ui32StepVarsSize].ui8Index = uStepVec - uBase; + ++ui32StepVarsSize; + } + + ++ui8Component; + auStepDirtyVecMask[uStepVec] >>= 1; + } + } + + psShader->sInfo.psTraceSteps[1 + uInstruction].ui32NumVariables = ui32StepVarsSize; + psShader->sInfo.psTraceSteps[1 + uInstruction].psVariables = psStepVars; + } +} + +void WriteTraceDeclarations(HLSLCrossCompilerContext* psContext) +{ + bstring glsl = *psContext->currentGLSLString; + + AddIndentation(psContext); + bcatcstr(glsl, "layout (std430) buffer Trace\n"); + AddIndentation(psContext); + bcatcstr(glsl, "{\n"); + ++psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "uint uTraceSize;\n"); + AddIndentation(psContext); + bcatcstr(glsl, "uint uTraceStride;\n"); + AddIndentation(psContext); + bcatcstr(glsl, "uint uTraceCapacity;\n"); + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + AddIndentation(psContext); + bcatcstr(glsl, "float fTracePixelCoordX;\n"); + AddIndentation(psContext); + bcatcstr(glsl, "float fTracePixelCoordY;\n"); + break; + case VERTEX_SHADER: + AddIndentation(psContext); + bcatcstr(glsl, "uint uTraceVertexID;\n"); + break; + default: + AddIndentation(psContext); + bcatcstr(glsl, "// Trace ID not implelemented for this shader type\n"); + break; + } + AddIndentation(psContext); + bcatcstr(glsl, "uint auTraceValues[];\n"); + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "};\n"); +} + +void WritePreStepsTrace(HLSLCrossCompilerContext* psContext, StepTraceInfo* psStep) +{ + uint32_t uVar; + bstring glsl = *psContext->currentGLSLString; + + AddIndentation(psContext); + bcatcstr(glsl, "bool bRecord = "); + switch (psContext->psShader->eShaderType) + { + case VERTEX_SHADER: + bcatcstr(glsl, "uint(gl_VertexID) == uTraceVertexID"); + break; + case PIXEL_SHADER: + bcatcstr(glsl, "max(abs(gl_FragCoord.x - fTracePixelCoordX), abs(gl_FragCoord.y - fTracePixelCoordY)) <= 0.5"); + break; + default: + bcatcstr(glsl, "/* Trace condition not implelemented for this shader type */"); + bcatcstr(glsl, "false"); + break; + } + bcatcstr(glsl, ";\n"); + + AddIndentation(psContext); + bcatcstr(glsl, "uint uTraceIndex = atomicAdd(uTraceSize, uTraceStride * (bRecord ? 1 : 0));\n"); + AddIndentation(psContext); + bcatcstr(glsl, "uint uTraceEnd = uTraceIndex + uTraceStride;\n"); + AddIndentation(psContext); + bcatcstr(glsl, "bRecord = bRecord && uTraceEnd <= uTraceCapacity;\n"); + AddIndentation(psContext); + bcatcstr(glsl, "uTraceEnd *= (bRecord ? 1 : 0);\n"); + + if (psStep->ui32NumVariables > 0) + { + AddIndentation(psContext); + bformata(glsl, "auTraceValues[min(++uTraceIndex, uTraceEnd)] = uint(0);\n"); // Adreno can't handle 0u (it's treated as int) + + for (uVar = 0; uVar < psStep->ui32NumVariables; ++uVar) + { + VariableTraceInfo* psVar = &psStep->psVariables[uVar]; + ASSERT(psVar->eGroup == TRACE_VARIABLE_INPUT); + if (psVar->eGroup == TRACE_VARIABLE_INPUT) + { + AddIndentation(psContext); + bcatcstr(glsl, "auTraceValues[min(++uTraceIndex, uTraceEnd)] = "); + + switch (psVar->eType) + { + case TRACE_VARIABLE_FLOAT: + bcatcstr(glsl, "floatBitsToUint("); + break; + case TRACE_VARIABLE_SINT: + bcatcstr(glsl, "uint("); + break; + case TRACE_VARIABLE_DOUBLE: + ASSERT(0); + // Not implemented yet; + break; + } + + bformata(glsl, "Input%d.%c", psVar->ui8Index, "xyzw"[psVar->ui8Component]); + + switch (psVar->eType) + { + case TRACE_VARIABLE_FLOAT: + case TRACE_VARIABLE_SINT: + bcatcstr(glsl, ")"); + break; + } + + bcatcstr(glsl, ";\n"); + } + } + } +} + +void WritePostStepTrace(HLSLCrossCompilerContext* psContext, uint32_t uStep) +{ + Instruction* psInstruction = psContext->psShader->psInst + uStep; + StepTraceInfo* psStep = psContext->psShader->sInfo.psTraceSteps + (1 + uStep); + + if (psStep->ui32NumVariables > 0) + { + uint32_t uVar; + + AddIndentation(psContext); + bformata(psContext->glsl, "auTraceValues[min(++uTraceIndex, uTraceEnd)] = %du;\n", uStep + 1); + + for (uVar = 0; uVar < psStep->ui32NumVariables; ++uVar) + { + VariableTraceInfo* psVar = &psStep->psVariables[uVar]; + uint16_t uOpcodeWriteMask = GetOpcodeWriteMask(psInstruction->eOpcode); + uint8_t uOperand = 0; + OPERAND_TYPE eOperandType = OPERAND_TYPE_NULL; + Operand* psOperand = NULL; + uint32_t uiIgnoreSwizzle = 0; + + switch (psVar->eGroup) + { + case TRACE_VARIABLE_TEMP: + eOperandType = OPERAND_TYPE_TEMP; + break; + case TRACE_VARIABLE_OUTPUT: + eOperandType = OPERAND_TYPE_OUTPUT; + break; + } + + if (psVar->eType == TRACE_VARIABLE_DOUBLE) + { + ASSERT(0); + // Not implemented yet + continue; + } + while (uOpcodeWriteMask) + { + if (uOpcodeWriteMask & 1) + { + if (eOperandType == psInstruction->asOperands[uOperand].eType && + psVar->ui8Index == psInstruction->asOperands[uOperand].ui32RegisterNumber) + { + psOperand = &psInstruction->asOperands[uOperand]; + break; + } + } + uOpcodeWriteMask >>= 1; + ++uOperand; + } + + if (psOperand == NULL) + { + ASSERT(0); + continue; + } + + AddIndentation(psContext); + bcatcstr(psContext->glsl, "auTraceValues[min(++uTraceIndex, uTraceEnd)] = "); + + TranslateVariableName(psContext, psOperand, TO_FLAG_UNSIGNED_INTEGER, &uiIgnoreSwizzle); + ASSERT(uiIgnoreSwizzle == 0); + + bformata(psContext->glsl, ".%c;\n", "xyzw"[psVar->ui8Component]); + } + } +} + +void WriteEndTrace(HLSLCrossCompilerContext* psContext) +{ + AddIndentation(psContext); + bcatcstr(psContext->glsl, "auTraceValues[min(++uTraceIndex, uTraceEnd)] = 0xFFFFFFFFu;\n"); +} + +int FindEmbeddedResourceName(EmbeddedResourceName* psEmbeddedName, HLSLCrossCompilerContext* psContext, bstring name) +{ + int offset = binstr(psContext->glsl, 0, name); + int size = name->slen; + + if (offset == BSTR_ERR || size > 0x3FF || offset > 0x7FFFF) + { + return 0; + } + + psEmbeddedName->ui20Offset = offset; + psEmbeddedName->ui12Size = size; + return 1; +} + +void IgnoreSampler(ShaderInfo* psInfo, uint32_t index) +{ + if (index + 1 < psInfo->ui32NumSamplers) + { + psInfo->asSamplers[index] = psInfo->asSamplers[psInfo->ui32NumSamplers - 1]; + } + --psInfo->ui32NumSamplers; +} + +void IgnoreResource(Resource* psResources, uint32_t* puSize, uint32_t index) +{ + if (index + 1 < *puSize) + { + psResources[index] = psResources[*puSize - 1]; + } + --*puSize; +} + +void FillInResourceDescriptions(HLSLCrossCompilerContext* psContext) +{ + uint32_t i; + bstring resourceName = bfromcstralloc(MAX_REFLECT_STRING_LENGTH, ""); + Shader* psShader = psContext->psShader; + + for (i = 0; i < psShader->sInfo.ui32NumSamplers; ++i) + { + Sampler* psSampler = psShader->sInfo.asSamplers + i; + SamplerMask* psMask = &psSampler->sMask; + if (psMask->bNormalSample || psMask->bCompareSample) + { + if (psMask->bNormalSample) + { + btrunc(resourceName, 0); + TextureName(resourceName, psShader, psMask->ui10TextureBindPoint, psMask->ui10SamplerBindPoint, 0); + if (!FindEmbeddedResourceName(&psSampler->sNormalName, psContext, resourceName)) + { + psMask->bNormalSample = 0; + } + } + if (psMask->bCompareSample) + { + btrunc(resourceName, 0); + TextureName(resourceName, psShader, psMask->ui10TextureBindPoint, psMask->ui10SamplerBindPoint, 1); + if (!FindEmbeddedResourceName(&psSampler->sCompareName, psContext, resourceName)) + { + psMask->bCompareSample = 0; + } + } + if (!psMask->bNormalSample && !psMask->bCompareSample) + { + IgnoreSampler(&psShader->sInfo, i); // Not used in the shader - ignore + } + } + else + { + btrunc(resourceName, 0); + TextureName(resourceName, psShader, psMask->ui10TextureBindPoint, psMask->ui10SamplerBindPoint, 0); + if (!FindEmbeddedResourceName(&psSampler->sNormalName, psContext, resourceName)) + { + IgnoreSampler(&psShader->sInfo, i); // Not used in the shader - ignore + } + } + } + + for (i = 0; i < psShader->sInfo.ui32NumImages; ++i) + { + Resource* psResources = psShader->sInfo.asImages; + uint32_t* puSize = &psShader->sInfo.ui32NumImages; + + Resource* psResource = psResources + i; + ResourceBinding* psBinding = NULL; + if (!GetResourceFromBindingPoint(psResource->eGroup, psResource->ui32BindPoint, &psShader->sInfo, &psBinding)) + { + ASSERT(0); + IgnoreResource(psResources, puSize, i); + } + + btrunc(resourceName, 0); + ConvertToUAVName(resourceName, psShader, psBinding->Name); + if (!FindEmbeddedResourceName(&psResource->sName, psContext, resourceName)) + { + IgnoreResource(psResources, puSize, i); + } + } + + for (i = 0; i < psShader->sInfo.ui32NumUniformBuffers; ++i) + { + Resource* psResources = psShader->sInfo.asUniformBuffers; + uint32_t* puSize = &psShader->sInfo.ui32NumUniformBuffers; + + Resource* psResource = psResources + i; + ConstantBuffer* psCB = NULL; + GetConstantBufferFromBindingPoint(psResource->eGroup, psResource->ui32BindPoint, &psShader->sInfo, &psCB); + + btrunc(resourceName, 0); + ConvertToUniformBufferName(resourceName, psShader, psCB->Name); + if (!FindEmbeddedResourceName(&psResource->sName, psContext, resourceName)) + { + IgnoreResource(psResources, puSize, i); + } + } + + for (i = 0; i < psShader->sInfo.ui32NumStorageBuffers; ++i) + { + Resource* psResources = psShader->sInfo.asStorageBuffers; + uint32_t* puSize = &psShader->sInfo.ui32NumStorageBuffers; + + Resource* psResource = psResources + i; + ConstantBuffer* psCB = NULL; + GetConstantBufferFromBindingPoint(psResource->eGroup, psResource->ui32BindPoint, &psShader->sInfo, &psCB); + + btrunc(resourceName, 0); + if (psResource->eGroup == RGROUP_UAV) + { + ConvertToUAVName(resourceName, psShader, psCB->Name); + } + else + { + ConvertToTextureName(resourceName, psShader, psCB->Name, NULL, 0); + } + if (!FindEmbeddedResourceName(&psResource->sName, psContext, resourceName)) + { + IgnoreResource(psResources, puSize, i); + } + } + + bdestroy(resourceName); +} + +GLLang ChooseLanguage(Shader* psShader) +{ + // Depends on the HLSL shader model extracted from bytecode. + switch (psShader->ui32MajorVersion) + { + case 5: + { + return LANG_430; + } + case 4: + { + return LANG_330; + } + default: + { + return LANG_120; + } + } +} + +const char* GetVersionString(GLLang language) +{ + switch (language) + { + case LANG_ES_100: + { + return "#version 100\n"; + break; + } + case LANG_ES_300: + { + return "#version 300 es\n"; + break; + } + case LANG_ES_310: + { + return "#version 310 es\n"; + break; + } + case LANG_120: + { + return "#version 120\n"; + break; + } + case LANG_130: + { + return "#version 130\n"; + break; + } + case LANG_140: + { + return "#version 140\n"; + break; + } + case LANG_150: + { + return "#version 150\n"; + break; + } + case LANG_330: + { + return "#version 330\n"; + break; + } + case LANG_400: + { + return "#version 400\n"; + break; + } + case LANG_410: + { + return "#version 410\n"; + break; + } + case LANG_420: + { + return "#version 420\n"; + break; + } + case LANG_430: + { + return "#version 430\n"; + break; + } + case LANG_440: + { + return "#version 440\n"; + break; + } + default: + { + return ""; + break; + } + } +} + +// Force precision of vertex output position to highp. +// Using mediump or lowp for the position of the vertex can cause rendering artifacts in OpenGL ES. +void ForcePositionOutputToHighp(Shader* shader) +{ + // Only sensible in vertex shaders + if (shader->eShaderType != VERTEX_SHADER) + { + return; + } + + // Find the output position declaration + Declaration* posDeclaration = NULL; + for (uint32_t i = 0; i < shader->ui32DeclCount; ++i) + { + Declaration* decl = shader->psDecl + i; + if (decl->eOpcode == OPCODE_DCL_OUTPUT_SIV) + { + if (decl->asOperands[0].eSpecialName == NAME_POSITION) + { + posDeclaration = decl; + break; + } + + if (decl->asOperands[0].eSpecialName != NAME_UNDEFINED) + { + continue; + } + + // This might be SV_Position (because d3dcompiler is weird). Get signature and check + InOutSignature *sig = NULL; + GetOutputSignatureFromRegister(decl->asOperands[0].ui32RegisterNumber, decl->asOperands[0].ui32CompMask, 0, &shader->sInfo, &sig); + ASSERT(sig != NULL); + if ((sig->eSystemValueType == NAME_POSITION || strcmp(sig->SemanticName, "POS") == 0) && sig->ui32SemanticIndex == 0) + { + sig->eMinPrec = MIN_PRECISION_DEFAULT; + posDeclaration = decl; + break; + } + } + else if (decl->eOpcode == OPCODE_DCL_OUTPUT) + { + InOutSignature *sig = NULL; + GetOutputSignatureFromRegister(decl->asOperands[0].ui32RegisterNumber, decl->asOperands[0].ui32CompMask, 0, &shader->sInfo, &sig); + ASSERT(sig != NULL); + if ((sig->eSystemValueType == NAME_POSITION || strcmp(sig->SemanticName, "POS") == 0) && sig->ui32SemanticIndex == 0) + { + sig->eMinPrec = MIN_PRECISION_DEFAULT; + posDeclaration = decl; + break; + } + } + } + + // Do nothing if we don't find suitable output. This may well be INTERNALTESSPOS for tessellation etc. + if (!posDeclaration) + { + return; + } + + posDeclaration->asOperands[0].eMinPrecision = OPERAND_MIN_PRECISION_DEFAULT; + posDeclaration->asOperands[0].eSpecialName = NAME_POSITION; + // Go through all the instructions and update the operand. + for (uint32_t i = 0; i < shader->ui32InstCount; ++i) + { + Instruction *inst = shader->psInst + i; + for (uint32_t j = 0; j < inst->ui32FirstSrc; ++j) + { + Operand op = inst->asOperands[j]; + // Since it's an output declaration we know that there's only one + // operand and it's in the first slot. + if (op.eType == OPERAND_TYPE_OUTPUT && op.ui32RegisterNumber == posDeclaration->asOperands[0].ui32RegisterNumber) + { + op.eMinPrecision = OPERAND_MIN_PRECISION_DEFAULT; + op.eSpecialName = NAME_POSITION; + } + } + } +} + +void TranslateToGLSL(HLSLCrossCompilerContext* psContext, GLLang* planguage, const GlExtensions* extensions) +{ + bstring glsl; + uint32_t i; + Shader* psShader = psContext->psShader; + GLLang language = *planguage; + const uint32_t ui32InstCount = psShader->ui32InstCount; + const uint32_t ui32DeclCount = psShader->ui32DeclCount; + + psContext->indent = 0; + + if (language == LANG_DEFAULT) + { + language = ChooseLanguage(psShader); + *planguage = language; + } + + glsl = bfromcstralloc (1024, ""); + if (!(psContext->flags & HLSLCC_FLAG_NO_VERSION_STRING)) + { + bcatcstr(glsl, GetVersionString(language)); + } + + if (psContext->flags & HLSLCC_FLAG_ADD_DEBUG_HEADER) + { + bstring version = glsl; + glsl = psContext->debugHeader; + bconcat(glsl, version); + bdestroy(version); + } + + psContext->glsl = glsl; + psContext->earlyMain = bfromcstralloc (1024, ""); + for (i = 0; i < NUM_PHASES; ++i) + { + psContext->postShaderCode[i] = bfromcstralloc (1024, ""); + } + + psContext->currentGLSLString = &glsl; + psShader->eTargetLanguage = language; + psShader->extensions = (const struct GlExtensions*)extensions; + psContext->currentPhase = MAIN_PHASE; + + if (extensions) + { + if (extensions->ARB_explicit_attrib_location) + { + bcatcstr(glsl, "#extension GL_ARB_explicit_attrib_location : require\n"); + } + if (extensions->ARB_explicit_uniform_location) + { + bcatcstr(glsl, "#extension GL_ARB_explicit_uniform_location : require\n"); + } + if (extensions->ARB_shading_language_420pack) + { + bcatcstr(glsl, "#extension GL_ARB_shading_language_420pack : require\n"); + } + } + + psContext->psShader->sInfo.ui32SymbolsOffset = blength(glsl); + + FRAMEBUFFER_FETCH_TYPE fetchType = CollectGmemInfo(psContext); + if (fetchType & FBF_EXT_COLOR) + { + bcatcstr(glsl, "#extension GL_EXT_shader_framebuffer_fetch : require\n"); + } + if (fetchType & FBF_ARM_COLOR) + { + bcatcstr(glsl, "#extension GL_ARM_shader_framebuffer_fetch : require\n"); + } + if (fetchType & (FBF_ARM_DEPTH | FBF_ARM_STENCIL)) + { + bcatcstr(glsl, "#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require\n"); + } + psShader->eGmemType = fetchType; + + AddVersionDependentCode(psContext); + + if (psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) + { + bcatcstr(glsl, "layout(std140) uniform;\n"); + } + + //Special case. Can have multiple phases. + if (psShader->eShaderType == HULL_SHADER) + { + int haveInstancedForkPhase = 0; + uint32_t forkIndex = 0; + + ConsolidateHullTempVars(psShader); + + for (i = 0; i < psShader->ui32HSDeclCount; ++i) + { + TranslateDeclaration(psContext, psShader->psHSDecl + i); + } + + //control + psContext->currentPhase = HS_CTRL_POINT_PHASE; + + if (psShader->ui32HSControlPointDeclCount) + { + bcatcstr(glsl, "//Control point phase declarations\n"); + for (i = 0; i < psShader->ui32HSControlPointDeclCount; ++i) + { + TranslateDeclaration(psContext, psShader->psHSControlPointPhaseDecl + i); + } + } + + if (psShader->ui32HSControlPointInstrCount) + { + SetDataTypes(psContext, psShader->psHSControlPointPhaseInstr, psShader->ui32HSControlPointInstrCount, NULL); + + bcatcstr(glsl, "void control_point_phase()\n{\n"); + psContext->indent++; + + for (i = 0; i < psShader->ui32HSControlPointInstrCount; ++i) + { + TranslateInstruction(psContext, psShader->psHSControlPointPhaseInstr + i); + } + psContext->indent--; + bcatcstr(glsl, "}\n"); + } + + //fork + psContext->currentPhase = HS_FORK_PHASE; + for (forkIndex = 0; forkIndex < psShader->ui32ForkPhaseCount; ++forkIndex) + { + bcatcstr(glsl, "//Fork phase declarations\n"); + for (i = 0; i < psShader->aui32HSForkDeclCount[forkIndex]; ++i) + { + TranslateDeclaration(psContext, psShader->apsHSForkPhaseDecl[forkIndex] + i); + if (psShader->apsHSForkPhaseDecl[forkIndex][i].eOpcode == OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT) + { + haveInstancedForkPhase = 1; + } + } + + bformata(glsl, "void fork_phase%d()\n{\n", forkIndex); + psContext->indent++; + + SetDataTypes(psContext, psShader->apsHSForkPhaseInstr[forkIndex], psShader->aui32HSForkInstrCount[forkIndex] - 1, NULL); + + if (haveInstancedForkPhase) + { + AddIndentation(psContext); + bformata(glsl, "for(int forkInstanceID = 0; forkInstanceID < HullPhase%dInstanceCount; ++forkInstanceID) {\n", forkIndex); + psContext->indent++; + } + + //The minus one here is remove the return statement at end of phases. + //This is needed otherwise the for loop will only run once. + ASSERT(psShader->apsHSForkPhaseInstr[forkIndex][psShader->aui32HSForkInstrCount[forkIndex] - 1].eOpcode == OPCODE_RET); + for (i = 0; i < psShader->aui32HSForkInstrCount[forkIndex] - 1; ++i) + { + TranslateInstruction(psContext, psShader->apsHSForkPhaseInstr[forkIndex] + i); + } + + if (haveInstancedForkPhase) + { + psContext->indent--; + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); +#endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); +#endif + } + } + + psContext->indent--; + bcatcstr(glsl, "}\n"); + } + + + //join + psContext->currentPhase = HS_JOIN_PHASE; + if (psShader->ui32HSJoinDeclCount) + { + bcatcstr(glsl, "//Join phase declarations\n"); + for (i = 0; i < psShader->ui32HSJoinDeclCount; ++i) + { + TranslateDeclaration(psContext, psShader->psHSJoinPhaseDecl + i); + } + } + + if (psShader->ui32HSJoinInstrCount) + { + SetDataTypes(psContext, psShader->psHSJoinPhaseInstr, psShader->ui32HSJoinInstrCount, NULL); + + bcatcstr(glsl, "void join_phase()\n{\n"); + psContext->indent++; + + for (i = 0; i < psShader->ui32HSJoinInstrCount; ++i) + { + TranslateInstruction(psContext, psShader->psHSJoinPhaseInstr + i); + } + + psContext->indent--; + bcatcstr(glsl, "}\n"); + } + + bcatcstr(glsl, "void main()\n{\n"); + + psContext->indent++; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Start Early Main ---\n"); +#endif + bconcat(glsl, psContext->earlyMain); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End Early Main ---\n"); +#endif + + if (psShader->ui32HSControlPointInstrCount) + { + AddIndentation(psContext); + bcatcstr(glsl, "control_point_phase();\n"); + + if (psShader->ui32ForkPhaseCount || psShader->ui32HSJoinInstrCount) + { + AddIndentation(psContext); + bcatcstr(glsl, "barrier();\n"); + } + } + for (forkIndex = 0; forkIndex < psShader->ui32ForkPhaseCount; ++forkIndex) + { + AddIndentation(psContext); + bformata(glsl, "fork_phase%d();\n", forkIndex); + + if (psShader->ui32HSJoinInstrCount || (forkIndex + 1 < psShader->ui32ForkPhaseCount)) + { + AddIndentation(psContext); + bcatcstr(glsl, "barrier();\n"); + } + } + if (psShader->ui32HSJoinInstrCount) + { + AddIndentation(psContext); + bcatcstr(glsl, "join_phase();\n"); + } + + psContext->indent--; + + bcatcstr(glsl, "}\n"); + + return; + } + + if (psShader->eShaderType == DOMAIN_SHADER) + { + uint32_t ui32TessOutPrimImp = AddImport(psContext, SYMBOL_TESSELLATOR_OUTPUT_PRIMITIVE, 0, (uint32_t)TESSELLATOR_OUTPUT_TRIANGLE_CCW); + uint32_t ui32TessPartitioningImp = AddImport(psContext, SYMBOL_TESSELLATOR_PARTITIONING, 0, (uint32_t)TESSELLATOR_PARTITIONING_INTEGER); + + bformata(glsl, "#if IMPORT_%d == %d\n", ui32TessOutPrimImp, (uint32_t)TESSELLATOR_OUTPUT_POINT); + bcatcstr(glsl, "layout(point_mode) in;\n"); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32TessOutPrimImp, (uint32_t)TESSELLATOR_OUTPUT_LINE); + bcatcstr(glsl, "layout(isolines) in;\n"); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32TessOutPrimImp, (uint32_t)TESSELLATOR_OUTPUT_TRIANGLE_CW); + bcatcstr(glsl, "layout(cw) in;\n"); + bcatcstr(glsl, "#endif\n"); + + bformata(glsl, "#if IMPORT_%d == %d\n", ui32TessPartitioningImp, (uint32_t)TESSELLATOR_PARTITIONING_FRACTIONAL_ODD); + bcatcstr(glsl, "layout(fractional_odd_spacing) in;\n"); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32TessPartitioningImp, (uint32_t)TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN); + bcatcstr(glsl, "layout(fractional_even_spacing) in;\n"); + bcatcstr(glsl, "#endif\n"); + } + + for (i = 0; i < ui32DeclCount; ++i) + { + TranslateDeclaration(psContext, psShader->psDecl + i); + } + + if (psContext->psShader->ui32NumDx9ImmConst) + { + bformata(psContext->glsl, "vec4 ImmConstArray [%d];\n", psContext->psShader->ui32NumDx9ImmConst); + } + + MarkIntegerImmediates(psContext); + + SetDataTypes(psContext, psShader->psInst, ui32InstCount, psContext->psShader->aeCommonTempVecType); + + if (psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING) + { + for (i = 0; i < MAX_TEMP_VEC4; ++i) + { + switch (psShader->aeCommonTempVecType[i]) + { + case SVT_VOID: + psShader->aeCommonTempVecType[i] = SVT_FLOAT; + case SVT_FLOAT: + case SVT_FLOAT10: + case SVT_FLOAT16: + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + bformata(psContext->glsl, "%s Temp%d", GetConstructorForTypeGLSL(psContext, psShader->aeCommonTempVecType[i], 4, true), i); + break; + case SVT_FORCE_DWORD: + // temp register not used + continue; + default: + continue; + } + + if (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) + { + bformata(psContext->glsl, "[1]"); + } + bformata(psContext->glsl, ";\n"); + } + + if (psContext->psShader->bUseTempCopy) + { + bcatcstr(psContext->glsl, "vec4 TempCopy;\n"); + bcatcstr(psContext->glsl, "uvec4 TempCopy_uint;\n"); + bcatcstr(psContext->glsl, "ivec4 TempCopy_int;\n"); + } + } + + // Declare auxiliary variables used to save intermediate results to bypass driver issues + SHADER_VARIABLE_TYPE auxVarType = SVT_UINT; + bformata(psContext->glsl, "highp %s %s1;\n", GetConstructorForTypeGLSL(psContext, auxVarType, 4, false), GetAuxArgumentName(auxVarType)); + + if (psContext->flags & HLSLCC_FLAG_TRACING_INSTRUMENTATION) + { + CreateTracingInfo(psShader); + WriteTraceDeclarations(psContext); + } + + bcatcstr(glsl, "void main()\n{\n"); + + psContext->indent++; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Start Early Main ---\n"); +#endif + bconcat(glsl, psContext->earlyMain); + if (psContext->flags & HLSLCC_FLAG_TRACING_INSTRUMENTATION) + { + WritePreStepsTrace(psContext, psShader->sInfo.psTraceSteps); + } +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End Early Main ---\n"); +#endif + + for (i = 0; i < ui32InstCount; ++i) + { + TranslateInstruction(psContext, psShader->psInst + i); + + if (psContext->flags & HLSLCC_FLAG_TRACING_INSTRUMENTATION) + { + WritePostStepTrace(psContext, i); + } + } + + psContext->indent--; + + bcatcstr(glsl, "}\n"); + + // Add exports + if (psShader->eShaderType == PIXEL_SHADER) + { + uint32_t ui32Input; + for (ui32Input = 0; ui32Input < MAX_SHADER_VEC4_INPUT; ++ui32Input) + { + INTERPOLATION_MODE eMode = psShader->sInfo.aePixelInputInterpolation[ui32Input]; + if (eMode != INTERPOLATION_LINEAR) + { + AddExport(psContext, SYMBOL_INPUT_INTERPOLATION_MODE, ui32Input, (uint32_t)eMode); + } + } + } + if (psShader->eShaderType == HULL_SHADER) + { + AddExport(psContext, SYMBOL_TESSELLATOR_PARTITIONING, 0, psShader->sInfo.eTessPartitioning); + AddExport(psContext, SYMBOL_TESSELLATOR_OUTPUT_PRIMITIVE, 0, psShader->sInfo.eTessOutPrim); + } + + FillInResourceDescriptions(psContext); +} + +static void FreeSubOperands(Instruction* psInst, const uint32_t ui32NumInsts) +{ + uint32_t ui32Inst; + for (ui32Inst = 0; ui32Inst < ui32NumInsts; ++ui32Inst) + { + Instruction* psCurrentInst = &psInst[ui32Inst]; + const uint32_t ui32NumOperands = psCurrentInst->ui32NumOperands; + uint32_t ui32Operand; + + for (ui32Operand = 0; ui32Operand < ui32NumOperands; ++ui32Operand) + { + uint32_t ui32SubOperand; + for (ui32SubOperand = 0; ui32SubOperand < MAX_SUB_OPERANDS; ++ui32SubOperand) + { + if (psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand]) + { + hlslcc_free(psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand]); + psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand] = NULL; + } + } + } + } +} + +void RemoveDoubleUnderscores(char* szName) +{ + char* position; + size_t length; + length = strlen(szName); + position = szName; + position = strstr(position, "__"); + while (position) + { + position[1] = '0'; + position += 2; + position = strstr(position, "__"); + } +} + +void RemoveDoubleUnderscoresFromIdentifiers(Shader* psShader) +{ + uint32_t i, j; + for (i = 0; i < psShader->sInfo.ui32NumConstantBuffers; ++i) + { + for (j = 0; j < psShader->sInfo.psConstantBuffers[i].ui32NumVars; ++j) + { + RemoveDoubleUnderscores(psShader->sInfo.psConstantBuffers[i].asVars[j].sType.Name); + } + } +} + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromMem(const char* shader, size_t size, unsigned int flags, GLLang language, const GlExtensions* extensions, GLSLShader* result) +{ + uint32_t* tokens; + Shader* psShader; + char* glslcstr = NULL; + int GLSLShaderType = GL_FRAGMENT_SHADER_ARB; + int success = 0; + uint32_t i; + + tokens = (uint32_t*)shader; + + psShader = DecodeDXBC(tokens); + + if (flags & (HLSLCC_FLAG_HASH_INPUT | HLSLCC_FLAG_ADD_DEBUG_HEADER)) + { + uint64_t ui64InputHash = hash64((const uint8_t*)tokens, tokens[6], 0); + psShader->sInfo.ui32InputHash = (uint32_t)ui64InputHash ^ (uint32_t)(ui64InputHash >> 32); + } + + RemoveDoubleUnderscoresFromIdentifiers(psShader); + + if (psShader) + { + ForcePositionOutputToHighp(psShader); + HLSLCrossCompilerContext sContext; + + sContext.psShader = psShader; + sContext.flags = flags; + + for (i = 0; i < NUM_PHASES; ++i) + { + sContext.havePostShaderCode[i] = 0; + } + + if (flags & HLSLCC_FLAG_ADD_DEBUG_HEADER) + { +#if defined(_WIN32) && !defined(PORTABLE) + ID3DBlob* pDisassembly = NULL; +#endif //defined(_WIN32) && !defined(PORTABLE) + + sContext.debugHeader = bformat("// HASH = 0x%08X\n", psShader->sInfo.ui32InputHash); + +#if defined(_WIN32) && !defined(PORTABLE) + D3DDisassemble(shader, size, 0, "", &pDisassembly); + bcatcstr(sContext.debugHeader, "/*\n"); + bcatcstr(sContext.debugHeader, (const char*)pDisassembly->lpVtbl->GetBufferPointer(pDisassembly)); + bcatcstr(sContext.debugHeader, "\n*/\n"); + pDisassembly->lpVtbl->Release(pDisassembly); +#endif //defined(_WIN32) && !defined(PORTABLE) + } + + TranslateToGLSL(&sContext, &language, extensions); + + switch (psShader->eShaderType) + { + case VERTEX_SHADER: + { + GLSLShaderType = GL_VERTEX_SHADER_ARB; + break; + } + case GEOMETRY_SHADER: + { + GLSLShaderType = GL_GEOMETRY_SHADER; + break; + } + case DOMAIN_SHADER: + { + GLSLShaderType = GL_TESS_EVALUATION_SHADER; + break; + } + case HULL_SHADER: + { + GLSLShaderType = GL_TESS_CONTROL_SHADER; + break; + } + case COMPUTE_SHADER: + { + GLSLShaderType = GL_COMPUTE_SHADER; + break; + } + default: + { + break; + } + } + + glslcstr = bstr2cstr(sContext.glsl, '\0'); + + bdestroy(sContext.glsl); + bdestroy(sContext.earlyMain); + for (i = 0; i < NUM_PHASES; ++i) + { + bdestroy(sContext.postShaderCode[i]); + } + + hlslcc_free(psShader->psHSControlPointPhaseDecl); + FreeSubOperands(psShader->psHSControlPointPhaseInstr, psShader->ui32HSControlPointInstrCount); + hlslcc_free(psShader->psHSControlPointPhaseInstr); + + for (i = 0; i < psShader->ui32ForkPhaseCount; ++i) + { + hlslcc_free(psShader->apsHSForkPhaseDecl[i]); + FreeSubOperands(psShader->apsHSForkPhaseInstr[i], psShader->aui32HSForkInstrCount[i]); + hlslcc_free(psShader->apsHSForkPhaseInstr[i]); + } + hlslcc_free(psShader->psHSJoinPhaseDecl); + FreeSubOperands(psShader->psHSJoinPhaseInstr, psShader->ui32HSJoinInstrCount); + hlslcc_free(psShader->psHSJoinPhaseInstr); + + hlslcc_free(psShader->psDecl); + FreeSubOperands(psShader->psInst, psShader->ui32InstCount); + hlslcc_free(psShader->psInst); + + memcpy(&result->reflection, &psShader->sInfo, sizeof(psShader->sInfo)); + + + hlslcc_free(psShader); + + success = 1; + } + + shader = 0; + tokens = 0; + + /* Fill in the result struct */ + + result->shaderType = GLSLShaderType; + result->sourceCode = glslcstr; + result->GLSLLanguage = language; + + return success; +} + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromFile(const char* filename, unsigned int flags, GLLang language, const GlExtensions* extensions, GLSLShader* result) +{ + FILE* shaderFile; + int length; + size_t readLength; + char* shader; + int success = 0; + + shaderFile = fopen(filename, "rb"); + + if (!shaderFile) + { + return 0; + } + + fseek(shaderFile, 0, SEEK_END); + length = ftell(shaderFile); + fseek(shaderFile, 0, SEEK_SET); + + shader = (char*)hlslcc_malloc(length + 1); + + readLength = fread(shader, 1, length, shaderFile); + + fclose(shaderFile); + shaderFile = 0; + + shader[readLength] = '\0'; + + success = TranslateHLSLFromMem(shader, readLength, flags, language, extensions, result); + + hlslcc_free(shader); + + return success; +} + +HLSLCC_API void HLSLCC_APIENTRY FreeGLSLShader(GLSLShader* s) +{ + bcstrfree(s->sourceCode); + s->sourceCode = NULL; + FreeShaderInfo(&s->reflection); +} + diff --git a/Code/Tools/HLSLCrossCompiler/src/toGLSLDeclaration.c b/Code/Tools/HLSLCrossCompiler/src/toGLSLDeclaration.c new file mode 100644 index 0000000000..3f08f2ab3d --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/toGLSLDeclaration.c @@ -0,0 +1,2908 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "hlslcc.h" +#include "internal_includes/toGLSLDeclaration.h" +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/languages.h" +#include "internal_includes/hlslccToolkit.h" +#include "bstrlib.h" +#include "internal_includes/debug.h" +#include +#include +#include + +#if !defined(isnan) +#ifdef _MSC_VER +#define isnan(x) _isnan(x) +#define isinf(x) (!_finite(x)) +#endif +#endif + +#define fpcheck(x) (isnan(x) || isinf(x)) + +typedef enum +{ + GLVARTYPE_FLOAT, + GLVARTYPE_INT, + GLVARTYPE_FLOAT4, +} GLVARTYPE; + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); +extern uint32_t AddImport(HLSLCrossCompilerContext* psContext, SYMBOL_TYPE eType, uint32_t ui32ID, uint32_t ui32Default); +extern uint32_t AddExport(HLSLCrossCompilerContext* psContext, SYMBOL_TYPE eType, uint32_t ui32ID, uint32_t ui32Value); + +const char* GetTypeString(GLVARTYPE eType) +{ + switch (eType) + { + case GLVARTYPE_FLOAT: + { + return "float"; + } + case GLVARTYPE_INT: + { + return "int"; + } + case GLVARTYPE_FLOAT4: + { + return "vec4"; + } + default: + { + return ""; + } + } +} +const uint32_t GetTypeElementCount(GLVARTYPE eType) +{ + switch (eType) + { + case GLVARTYPE_FLOAT: + case GLVARTYPE_INT: + { + return 1; + } + case GLVARTYPE_FLOAT4: + { + return 4; + } + default: + { + return 0; + } + } +} + +void GetSTD140Layout(ShaderVarType* pType, uint32_t* puAlignment, uint32_t* puSize) +{ + *puSize = 0; + *puAlignment = 1; + switch (pType->Type) + { + case SVT_BOOL: + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + case SVT_FLOAT: + case SVT_FLOAT10: + case SVT_FLOAT16: + *puSize = 4; + *puAlignment = 4; + break; + case SVT_DOUBLE: + *puSize = 8; + *puAlignment = 4; + break; + case SVT_VOID: + break; + default: + ASSERT(0); + break; + } + switch (pType->Class) + { + case SVC_SCALAR: + break; + case SVC_MATRIX_ROWS: + case SVC_MATRIX_COLUMNS: + // Matrices are translated to arrays of vectors + *puSize *= pType->Rows; + case SVC_VECTOR: + switch (pType->Columns) + { + case 2: + *puSize *= 2; + *puAlignment *= 2; + break; + case 3: + case 4: + *puSize *= 4; + *puAlignment *= 4; + break; + } + break; + case SVC_STRUCT: + { + uint32_t uMember; + for (uMember = 0; uMember < pType->MemberCount; ++uMember) + { + uint32_t uMemberAlignment, uMemberSize; + *puSize += pType->Members[uMember].Offset; + GetSTD140Layout(pType->Members + uMember, &uMemberAlignment, &uMemberSize); + *puSize += uMemberAlignment - 1; + *puSize -= *puSize % uMemberAlignment; + *puAlignment = *puAlignment > uMemberAlignment ? *puAlignment : uMemberAlignment; + } + } + break; + default: + ASSERT(0); + break; + } + + if (pType->Elements > 1) + { + *puSize *= pType->Elements; + } + + if (pType->Elements > 1 || pType->Class == SVC_MATRIX_ROWS || pType->Class == SVC_MATRIX_COLUMNS) + { + *puAlignment = (*puAlignment + 0x0000000F) & 0xFFFFFFF0; + } +} + +void AddToDx9ImmConstIndexableArray(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + bstring* savedStringPtr = psContext->currentGLSLString; + + psContext->currentGLSLString = &psContext->earlyMain; + psContext->indent++; + AddIndentation(psContext); + psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber] = psContext->psShader->ui32NumDx9ImmConst; + bformata(psContext->earlyMain, "ImmConstArray[%d] = ", psContext->psShader->ui32NumDx9ImmConst); + TranslateOperand(psContext, psOperand, TO_FLAG_NONE); + bcatcstr(psContext->earlyMain, ";\n"); + psContext->indent--; + psContext->psShader->ui32NumDx9ImmConst++; + + psContext->currentGLSLString = savedStringPtr; +} + +void DeclareConstBufferShaderVariable(HLSLCrossCompilerContext* psContext, const char* Name, const struct ShaderVarType_TAG* psType, int unsizedArray) +{ + bstring glsl = *psContext->currentGLSLString; + + if (psType->Class == SVC_STRUCT) + { + bcatcstr(glsl, "\t"); + ShaderVarName(glsl, psContext->psShader, Name); + bcatcstr(glsl, "_Type "); + ShaderVarName(glsl, psContext->psShader, Name); + if (psType->Elements > 1) + { + bformata(glsl, "[%d]", psType->Elements); + } + } + else if (psType->Class == SVC_MATRIX_COLUMNS || psType->Class == SVC_MATRIX_ROWS) + { + switch (psType->Type) + { + case SVT_FLOAT: + { + bformata(glsl, "\tvec%d ", psType->Columns); + ShaderVarName(glsl, psContext->psShader, Name); + bformata(glsl, "[%d", psType->Rows); + break; + } + default: + { + ASSERT(0); + break; + } + } + if (psType->Elements > 1) + { + bformata(glsl, " * %d", psType->Elements); + } + bformata(glsl, "]"); + } + else + if (psType->Class == SVC_VECTOR) + { + switch (psType->Type) + { + default: + ASSERT(0); + case SVT_FLOAT: + case SVT_FLOAT10: + case SVT_FLOAT16: + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + bformata(glsl, "\t%s ", GetConstructorForTypeGLSL(psContext, psType->Type, psType->Columns, true)); + break; + case SVT_DOUBLE: + bformata(glsl, "\tdvec%d ", psType->Columns); + break; + } + + ShaderVarName(glsl, psContext->psShader, Name); + + if (psType->Elements > 1) + { + bformata(glsl, "[%d]", psType->Elements); + } + } + else + if (psType->Class == SVC_SCALAR) + { + switch (psType->Type) + { + default: + ASSERT(0); + case SVT_FLOAT: + case SVT_FLOAT10: + case SVT_FLOAT16: + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + bformata(glsl, "\t%s ", GetConstructorForTypeGLSL(psContext, psType->Type, 1, true)); + break; + case SVT_DOUBLE: + bformata(glsl, "\tdouble "); + break; + case SVT_BOOL: + //Use int instead of bool. + //Allows implicit conversions to integer and + //bool consumes 4-bytes in HLSL and GLSL anyway. + bformata(glsl, "\tint "); + break; + } + + ShaderVarName(glsl, psContext->psShader, Name); + + if (psType->Elements > 1) + { + bformata(glsl, "[%d]", psType->Elements); + } + } + if (unsizedArray) + { + bformata(glsl, "[]"); + } + bformata(glsl, ";\n"); +} + +//In GLSL embedded structure definitions are not supported. +void PreDeclareStructType(HLSLCrossCompilerContext* psContext, const char* Name, const struct ShaderVarType_TAG* psType) +{ + uint32_t i; + bstring glsl = *psContext->currentGLSLString; + + for (i = 0; i < psType->MemberCount; ++i) + { + if (psType->Members[i].Class == SVC_STRUCT) + { + PreDeclareStructType(psContext, psType->Members[i].Name, &psType->Members[i]); + } + } + + if (psType->Class == SVC_STRUCT) + { +#if !defined(NDEBUG) + uint32_t unnamed_struct = strcmp(Name, "$Element") == 0 ? 1 : 0; +#endif + //Not supported at the moment + ASSERT(!unnamed_struct); + + bcatcstr(glsl, "struct "); + ShaderVarName(glsl, psContext->psShader, Name); + bcatcstr(glsl, "_Type {\n"); + + for (i = 0; i < psType->MemberCount; ++i) + { + ASSERT(psType->Members != 0); + + DeclareConstBufferShaderVariable(psContext, psType->Members[i].Name, &psType->Members[i], 0); + } + + bformata(glsl, "};\n"); + } +} + +void DeclarePLSStructVars(HLSLCrossCompilerContext* psContext, const char* Name, const struct ShaderVarType_TAG* psType) +{ + (void)Name; + + uint32_t i; + bstring glsl = *psContext->currentGLSLString; + + ASSERT(psType->Members != 0); + + for (i = 0; i < psType->MemberCount; ++i) + { + if (psType->Members[i].Class == SVC_STRUCT) + { + ASSERT(0); // PLS can't have nested structs + } + } + + if (psType->Class == SVC_STRUCT) + { + for (i = 0; i < psType->MemberCount; ++i) + { + ShaderVarType cur_member = psType->Members[i]; + + if (cur_member.Class == SVC_VECTOR) + { + switch (cur_member.Type) + { + case SVT_FLOAT: + { + // float2 -> rg16f + if (2 == cur_member.Columns) + { + bcatcstr(glsl, "\tlayout(rg16f) highp vec2 "); + } + // float3 -> r11f_g11f_b10f + else if (3 == cur_member.Columns) + { + bcatcstr(glsl, "\tlayout(r11f_g11f_b10f) highp vec3 "); + } + // float4 -> rgba8 + else if (4 == cur_member.Columns) + { + bcatcstr(glsl, "\tlayout(rgba8) highp vec4 "); + } + else + { + ASSERT(0); // not supported + } + break; + } + case SVT_INT: + { + // int2 -> rg16i + if (2 == cur_member.Columns) + { + bcatcstr(glsl, "\tlayout(rg16i) highp ivec2 "); + } + // int4 -> rgba8i + else if (4 == cur_member.Columns) + { + bcatcstr(glsl, "\tlayout(rgba8i) highp ivec4 "); + } + else + { + ASSERT(0); // not supported + } + break; + } + case SVT_UINT: + case SVT_DOUBLE: + default: + ASSERT(0); + } + + if (cur_member.Elements > 1) + { + ASSERT(0); // PLS can't have arrays + } + } + else if (cur_member.Class == SVC_SCALAR) + { + switch (cur_member.Type) + { + case SVT_UINT: + bcatcstr(glsl, "\tlayout(r32ui) highp uint "); + break; + case SVT_FLOAT: + case SVT_INT: + case SVT_DOUBLE: + case SVT_BOOL: + default: + ASSERT(0); + } + } + + ShaderVarName(glsl, psContext->psShader, cur_member.Name); + bcatcstr(glsl, ";\n"); + } + } + else + { + ASSERT(0); + } +} + +char* GetDeclaredInputName(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand) +{ + bstring inputName; + char* cstr; + InOutSignature* psIn; + + if (eShaderType == GEOMETRY_SHADER) + { + inputName = bformat("VtxOutput%d", psOperand->ui32RegisterNumber); + } + else if (eShaderType == HULL_SHADER) + { + inputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + else if (eShaderType == DOMAIN_SHADER) + { + inputName = bformat("HullOutput%d", psOperand->ui32RegisterNumber); + } + else if (eShaderType == PIXEL_SHADER) + { + if (psContext->flags & HLSLCC_FLAG_TESS_ENABLED) + { + inputName = bformat("DomOutput%d", psOperand->ui32RegisterNumber); + } + else + { + inputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + } + else + { + ASSERT(eShaderType == VERTEX_SHADER); + inputName = bformat("dcl_Input%d", psOperand->ui32RegisterNumber); + } + if ((psContext->flags & HLSLCC_FLAG_INOUT_SEMANTIC_NAMES) && GetInputSignatureFromRegister(psOperand->ui32RegisterNumber, &psContext->psShader->sInfo, &psIn)) + { + bformata(inputName, "_%s%d", psIn->SemanticName, psIn->ui32SemanticIndex); + } + + cstr = bstr2cstr(inputName, '\0'); + bdestroy(inputName); + return cstr; +} + +char* GetDeclaredOutputName(const HLSLCrossCompilerContext* psContext, + const SHADER_TYPE eShaderType, + const Operand* psOperand, + int* piStream) +{ + bstring outputName; + char* cstr; + InOutSignature* psOut; + + int foundOutput = GetOutputSignatureFromRegister(psOperand->ui32RegisterNumber, + psOperand->ui32CompMask, + psContext->psShader->ui32CurrentVertexOutputStream, + &psContext->psShader->sInfo, + &psOut); + + (void)(foundOutput); + ASSERT(foundOutput); + + if (eShaderType == GEOMETRY_SHADER) + { + if (psOut->ui32Stream != 0) + { + outputName = bformat("VtxGeoOutput%d_S%d", psOperand->ui32RegisterNumber, psOut->ui32Stream); + piStream[0] = psOut->ui32Stream; + } + else + { + outputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + } + else if (eShaderType == DOMAIN_SHADER) + { + outputName = bformat("DomOutput%d", psOperand->ui32RegisterNumber); + } + else if (eShaderType == VERTEX_SHADER) + { + if (psContext->flags & HLSLCC_FLAG_GS_ENABLED) + { + outputName = bformat("VtxOutput%d", psOperand->ui32RegisterNumber); + } + else + { + outputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + } + else if (eShaderType == PIXEL_SHADER) + { + outputName = bformat("PixOutput%d", psOperand->ui32RegisterNumber); + } + else + { + ASSERT(eShaderType == HULL_SHADER); + outputName = bformat("HullOutput%d", psOperand->ui32RegisterNumber); + } + if (psContext->flags & HLSLCC_FLAG_INOUT_SEMANTIC_NAMES) + { + bformata(outputName, "_%s%d", psOut->SemanticName, psOut->ui32SemanticIndex); + } + + cstr = bstr2cstr(outputName, '\0'); + bdestroy(outputName); + return cstr; +} +static void DeclareInput( + HLSLCrossCompilerContext* psContext, + const Declaration* psDecl, + const char* Interpolation, const char* StorageQualifier, const char* Precision, int iNumComponents, OPERAND_INDEX_DIMENSION eIndexDim, const char* InputName) +{ + Shader* psShader = psContext->psShader; + bstring glsl = *psContext->currentGLSLString; + + // This falls within the specified index ranges. The default is 0 if no input range is specified + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + { + return; + } + + if (psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == 0) + { + const char* vecType = "vec"; + const char* scalarType = "float"; + InOutSignature* psSignature = NULL; + + if (GetInputSignatureFromRegister(psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psSignature)) + { + switch (psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + vecType = "uvec"; + scalarType = "uint"; + break; + } + case INOUT_COMPONENT_SINT32: + { + vecType = "ivec"; + scalarType = "int"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + } + + if (psShader->eShaderType == PIXEL_SHADER) + { + psShader->sInfo.aePixelInputInterpolation[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.eInterpolation; + } + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) || + (psShader->eShaderType == VERTEX_SHADER && HaveLimitedInOutLocationQualifier(psContext->psShader->eTargetLanguage))) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + switch (eIndexDim) + { + case INDEX_2D: + { + if (iNumComponents == 1) + { + const uint32_t arraySize = psDecl->asOperands[0].aui32ArraySizes[0]; + + psContext->psShader->abScalarInput[psDecl->asOperands[0].ui32RegisterNumber] = -1; + + bformata(glsl, "%s %s %s %s [%d];\n", StorageQualifier, Precision, scalarType, InputName, arraySize); + + bformata(glsl, "%s1 Input%d;\n", vecType, psDecl->asOperands[0].ui32RegisterNumber); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = arraySize; + } + else + { + bformata(glsl, "%s %s %s%d %s [%d];\n", StorageQualifier, Precision, vecType, iNumComponents, InputName, psDecl->asOperands[0].aui32ArraySizes[0]); + + bformata(glsl, "%s %s%d Input%d[%d];\n", Precision, vecType, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].aui32ArraySizes[0]); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->asOperands[0].aui32ArraySizes[0]; + } + break; + } + default: + { + if (psDecl->asOperands[0].eType == OPERAND_TYPE_SPECIAL_TEXCOORD) + { + InputName = "TexCoord"; + } + + if (iNumComponents == 1) + { + psContext->psShader->abScalarInput[psDecl->asOperands[0].ui32RegisterNumber] = 1; + + bformata(glsl, "%s %s %s %s %s;\n", Interpolation, StorageQualifier, Precision, scalarType, InputName); + bformata(glsl, "%s1 Input%d;\n", vecType, psDecl->asOperands[0].ui32RegisterNumber); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = -1; + } + else + { + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] > 0) + { + bformata(glsl, "%s %s %s %s%d %s", Interpolation, StorageQualifier, Precision, vecType, iNumComponents, InputName); + bformata(glsl, "[%d];\n", psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]); + + bformata(glsl, "%s %s%d Input%d[%d];\n", Precision, vecType, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber, psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]; + } + else + { + bformata(glsl, "%s %s %s %s%d %s;\n", Interpolation, StorageQualifier, Precision, vecType, iNumComponents, InputName); + bformata(glsl, "%s %s%d Input%d;\n", Precision, vecType, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = -1; + } + } + break; + } + } + } + + if (psShader->abInputReferencedByInstruction[psDecl->asOperands[0].ui32RegisterNumber]) + { + psContext->currentGLSLString = &psContext->earlyMain; + psContext->indent++; + + if (psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == -1) //Not an array + { + AddIndentation(psContext); + bformata(psContext->earlyMain, "Input%d = %s;\n", psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + else + { + int arrayIndex = psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber]; + + while (arrayIndex) + { + AddIndentation(psContext); + bformata(psContext->earlyMain, "Input%d[%d] = %s[%d];\n", psDecl->asOperands[0].ui32RegisterNumber, arrayIndex - 1, InputName, arrayIndex - 1); + + arrayIndex--; + } + } + psContext->indent--; + psContext->currentGLSLString = &psContext->glsl; + } +} + +void AddBuiltinInput(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, const char* builtinName, uint32_t uNumComponents) +{ + (void)uNumComponents; + + bstring glsl = *psContext->currentGLSLString; + Shader* psShader = psContext->psShader; + + if (psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == 0) + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, &psDecl->asOperands[0]); + bformata(glsl, "%s ", GetConstructorForTypeGLSL(psContext, eType, 4, false)); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bformata(glsl, ";\n"); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = 1; + } + else + { + //This register has already been declared. The HLSL bytecode likely looks + //something like this then: + // dcl_input_ps constant v3.x + // dcl_input_ps_sgv v3.y, primitive_id + + //GLSL does not allow assignment to a varying! + } + + psContext->currentGLSLString = &psContext->earlyMain; + psContext->indent++; + AddIndentation(psContext); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + + bformata(psContext->earlyMain, " = %s", builtinName); + + switch (psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + TranslateOperandSwizzle(psContext, &psDecl->asOperands[0]); + // Invert w coordinate if necessary to be the same as SV_Position + if (psContext->psShader->eShaderType == PIXEL_SHADER) + { + if (psDecl->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE && + psDecl->asOperands[0].eType == OPERAND_TYPE_INPUT) + { + if (psDecl->asOperands[0].ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + uint32_t ui32IgnoreSwizzle; + bcatcstr(psContext->earlyMain, ";\n#ifdef EMULATE_DEPTH_CLAMP\n"); + AddIndentation(psContext); + TranslateVariableName(psContext, &psDecl->asOperands[0], TO_FLAG_NONE, &ui32IgnoreSwizzle); + bcatcstr(psContext->earlyMain, ".z = unclampedDepth;\n"); + bcatcstr(psContext->earlyMain, "#endif\n"); + } + if (psDecl->asOperands[0].ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + uint32_t ui32IgnoreSwizzle; + bcatcstr(psContext->earlyMain, ";\n"); + AddIndentation(psContext); + TranslateVariableName(psContext, &psDecl->asOperands[0], TO_FLAG_NONE, &ui32IgnoreSwizzle); + bcatcstr(psContext->earlyMain, ".w = 1.0 / "); + TranslateVariableName(psContext, &psDecl->asOperands[0], TO_FLAG_NONE, &ui32IgnoreSwizzle); + bcatcstr(psContext->earlyMain, ".w;\n"); + } + } + else + { + ASSERT(0); + } + } + + break; + default: + //Scalar built-in. Don't apply swizzle. + break; + } + bcatcstr(psContext->earlyMain, ";\n"); + + psContext->indent--; + psContext->currentGLSLString = &psContext->glsl; +} + +int OutputNeedsDeclaring(HLSLCrossCompilerContext* psContext, const Operand* psOperand, const int count) +{ + Shader* psShader = psContext->psShader; + + // Depth Output operands are a special case and won't have a ui32RegisterNumber, + // so first we have to check if the output operand is depth. + if (psShader->eShaderType == PIXEL_SHADER) + { + if (psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL) + { + return 1; + } + else if (psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH) + { + return 0; // OpenGL doesn't need to declare depth output variable (gl_FragDepth) + } + } + + const uint32_t declared = ((psContext->currentPhase + 1) << 3) | psShader->ui32CurrentVertexOutputStream; + ASSERT(psOperand->ui32RegisterNumber >= 0); + ASSERT(psOperand->ui32RegisterNumber < MAX_SHADER_VEC4_OUTPUT); + if (psShader->aiOutputDeclared[psOperand->ui32RegisterNumber] != declared) + { + int offset; + + for (offset = 0; offset < count; offset++) + { + psShader->aiOutputDeclared[psOperand->ui32RegisterNumber + offset] = declared; + } + return 1; + } + + return 0; +} + +void AddBuiltinOutput(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, const GLVARTYPE type, int arrayElements, const char* builtinName) +{ + bstring glsl = *psContext->currentGLSLString; + Shader* psShader = psContext->psShader; + + psContext->havePostShaderCode[psContext->currentPhase] = 1; + + if (OutputNeedsDeclaring(psContext, &psDecl->asOperands[0], arrayElements ? arrayElements : 1)) + { + InOutSignature* psSignature = NULL; + + GetOutputSignatureFromRegister(psDecl->asOperands[0].ui32RegisterNumber, + psDecl->asOperands[0].ui32CompMask, + 0, + &psShader->sInfo, &psSignature); + + bcatcstr(glsl, "#undef "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "\n"); + + bcatcstr(glsl, "#define "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bformata(glsl, " phase%d_", psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "\n"); + + bcatcstr(glsl, "vec4 "); + bformata(glsl, "phase%d_", psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + if (arrayElements) + { + bformata(glsl, "[%d];\n", arrayElements); + } + else + { + bcatcstr(glsl, ";\n"); + } + + psContext->currentGLSLString = &psContext->postShaderCode[psContext->currentPhase]; + glsl = *psContext->currentGLSLString; + psContext->indent++; + if (arrayElements) + { + int elem; + for (elem = 0; elem < arrayElements; elem++) + { + AddIndentation(psContext); + bformata(glsl, "%s[%d] = %s(phase%d_", builtinName, elem, GetTypeString(type), psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bformata(glsl, "[%d]", elem); + TranslateOperandSwizzle(psContext, &psDecl->asOperands[0]); + bformata(glsl, ");\n"); + } + } + else + { + if (psDecl->asOperands[0].eSpecialName == NAME_CLIP_DISTANCE) + { + int max = GetMaxComponentFromComponentMask(&psDecl->asOperands[0]); + + int applySiwzzle = GetNumSwizzleElements(&psDecl->asOperands[0]) > 1 ? 1 : 0; + int index; + int i; + int multiplier = 1; + char* swizzle[] = {".x", ".y", ".z", ".w"}; + + ASSERT(psSignature != NULL); + + index = psSignature->ui32SemanticIndex; + + //Clip distance can be spread across 1 or 2 outputs (each no more than a vec4). + //Some examples: + //float4 clip[2] : SV_ClipDistance; //8 clip distances + //float3 clip[2] : SV_ClipDistance; //6 clip distances + //float4 clip : SV_ClipDistance; //4 clip distances + //float clip : SV_ClipDistance; //1 clip distance. + + //In GLSL the clip distance built-in is an array of up to 8 floats. + //So vector to array conversion needs to be done here. + if (index == 1) + { + InOutSignature* psFirstClipSignature; + if (GetOutputSignatureFromSystemValue(NAME_CLIP_DISTANCE, 1, &psShader->sInfo, &psFirstClipSignature)) + { + if (psFirstClipSignature->ui32Mask & (1 << 3)) + { + multiplier = 4; + } + else + if (psFirstClipSignature->ui32Mask & (1 << 2)) + { + multiplier = 3; + } + else + if (psFirstClipSignature->ui32Mask & (1 << 1)) + { + multiplier = 2; + } + } + } + + for (i = 0; i < max; ++i) + { + AddIndentation(psContext); + bformata(glsl, "%s[%d] = (phase%d_", builtinName, i + multiplier * index, psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + if (applySiwzzle) + { + bformata(glsl, ")%s;\n", swizzle[i]); + } + else + { + bformata(glsl, ");\n"); + } + } + } + else + { + uint32_t elements = GetNumSwizzleElements(&psDecl->asOperands[0]); + + if (elements != GetTypeElementCount(type)) + { + //This is to handle float3 position seen in control point phases + //struct HS_OUTPUT + //{ + // float3 vPosition : POSITION; + //}; -> dcl_output o0.xyz + //gl_Position is vec4. + AddIndentation(psContext); + bformata(glsl, "%s = %s(phase%d_", builtinName, GetTypeString(type), psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, ", 1);\n"); + } + else + { + AddIndentation(psContext); + bformata(glsl, "%s = %s(phase%d_", builtinName, GetTypeString(type), psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, ");\n"); + } + } + + if (psShader->eShaderType == VERTEX_SHADER && psDecl->asOperands[0].eSpecialName == NAME_POSITION) + { + if (psContext->flags & HLSLCC_FLAG_INVERT_CLIP_SPACE_Y) + { + AddIndentation(psContext); + bformata(glsl, "gl_Position.y = -gl_Position.y;\n"); + } + + if (EmulateDepthClamp(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "#ifdef EMULATE_DEPTH_CLAMP\n"); + bcatcstr(glsl, "#if EMULATE_DEPTH_CLAMP == 1\n"); + AddIndentation(psContext); + bcatcstr(glsl, "unclampedDepth = gl_DepthRange.near + gl_DepthRange.diff * gl_Position.z / gl_Position.w;\n"); + bcatcstr(glsl, "#elif EMULATE_DEPTH_CLAMP == 2\n"); + AddIndentation(psContext); + bcatcstr(glsl, "unclampedZ = gl_DepthRange.diff * gl_Position.z;\n"); + bcatcstr(glsl, "#endif\n"); + AddIndentation(psContext); + bcatcstr(glsl, "gl_Position.z = 0.0;\n"); + } + + if (psContext->flags & HLSLCC_FLAG_CONVERT_CLIP_SPACE_Z) + { + if (EmulateDepthClamp(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "#else\n"); + } + + AddIndentation(psContext); + bcatcstr(glsl, "gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n"); + } + + if (EmulateDepthClamp(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "#endif\n"); + } + } + } + psContext->indent--; + psContext->currentGLSLString = &psContext->glsl; + } +} + +void AddUserOutput(HLSLCrossCompilerContext* psContext, const Declaration* psDecl) +{ + bstring glsl = *psContext->currentGLSLString; + Shader* psShader = psContext->psShader; + + if (OutputNeedsDeclaring(psContext, &psDecl->asOperands[0], 1)) + { + const Operand* psOperand = &psDecl->asOperands[0]; + const char* Precision = ""; + const char* type = "vec"; + + InOutSignature* psSignature = NULL; + + GetOutputSignatureFromRegister(psDecl->asOperands[0].ui32RegisterNumber, + psDecl->asOperands[0].ui32CompMask, + psShader->ui32CurrentVertexOutputStream, + &psShader->sInfo, + &psSignature); + + switch (psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + type = "uvec"; + break; + } + case INOUT_COMPONENT_SINT32: + { + type = "ivec"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + + if (HavePrecisionQualifers(psShader->eTargetLanguage)) + { + switch (psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + { + Precision = "highp"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_2_8: + { + Precision = "lowp"; + break; + } + case OPERAND_MIN_PRECISION_SINT_16: + { + Precision = "mediump"; + //type = "ivec"; + break; + } + case OPERAND_MIN_PRECISION_UINT_16: + { + Precision = "mediump"; + //type = "uvec"; + break; + } + } + } + + switch (psShader->eShaderType) + { + case PIXEL_SHADER: + { + switch (psDecl->asOperands[0].eType) + { + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + case OPERAND_TYPE_OUTPUT_DEPTH: + { + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + { + bcatcstr(glsl, "#ifdef GL_ARB_conservative_depth\n"); + bcatcstr(glsl, "#extension GL_ARB_conservative_depth : enable\n"); + bcatcstr(glsl, "layout (depth_greater) out float gl_FragDepth;\n"); + bcatcstr(glsl, "#endif\n"); + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + { + bcatcstr(glsl, "#ifdef GL_ARB_conservative_depth\n"); + bcatcstr(glsl, "#extension GL_ARB_conservative_depth : enable\n"); + bcatcstr(glsl, "layout (depth_less) out float gl_FragDepth;\n"); + bcatcstr(glsl, "#endif\n"); + break; + } + default: + { + if (WriteToFragData(psContext->psShader->eTargetLanguage)) + { + bformata(glsl, "#define Output%d gl_FragData[%d]\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber); + } + else + { + int stream = 0; + char* OutputName = GetDeclaredOutputName(psContext, PIXEL_SHADER, psOperand, &stream); + + uint32_t renderTarget = psDecl->asOperands[0].ui32RegisterNumber; + + // Check if we already defined this as a "inout" + if ((psContext->rendertargetUse[renderTarget] & INPUT_RENDERTARGET) == 0) + { + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) || HaveLimitedInOutLocationQualifier(psContext->psShader->eTargetLanguage)) + { + uint32_t index = 0; + + if ((psContext->flags & HLSLCC_FLAG_DUAL_SOURCE_BLENDING) && DualSourceBlendSupported(psContext->psShader->eTargetLanguage)) + { + if (renderTarget > 0) + { + renderTarget = 0; + index = 1; + } + bformata(glsl, "layout(location = %d, index = %d) ", renderTarget, index); + } + else + { + bformata(glsl, "layout(location = %d) ", renderTarget); + } + } + + bformata(glsl, "out %s %s4 %s;\n", Precision, type, OutputName); + } + + if (stream) + { + bformata(glsl, "#define Output%d_S%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, stream, OutputName); + } + else + { + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + } + bcstrfree(OutputName); + } + break; + } + } + break; + } + case VERTEX_SHADER: + { + int iNumComponents = 4; //GetMaxComponentFromComponentMask(&psDecl->asOperands[0]); + int stream = 0; + char* OutputName = GetDeclaredOutputName(psContext, VERTEX_SHADER, psOperand, &stream); + + if (psShader->eShaderType == VERTEX_SHADER) + { + uint32_t ui32InterpImp = AddImport(psContext, SYMBOL_INPUT_INTERPOLATION_MODE, psDecl->asOperands[0].ui32RegisterNumber, (uint32_t)INTERPOLATION_LINEAR); + bformata(glsl, "#if IMPORT_%d == %d\n", ui32InterpImp, INTERPOLATION_CONSTANT); + bformata(glsl, "#define Output%dInterpolation flat\n", psDecl->asOperands[0].ui32RegisterNumber); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32InterpImp, INTERPOLATION_LINEAR_CENTROID); + bformata(glsl, "#define Output%dInterpolation centroid\n", psDecl->asOperands[0].ui32RegisterNumber); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32InterpImp, INTERPOLATION_LINEAR_NOPERSPECTIVE); + bformata(glsl, "#define Output%dInterpolation noperspective\n", psDecl->asOperands[0].ui32RegisterNumber); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32InterpImp, INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID); + bformata(glsl, "#define Output%dInterpolation noperspective centroid\n", psDecl->asOperands[0].ui32RegisterNumber); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32InterpImp, INTERPOLATION_LINEAR_SAMPLE); + bformata(glsl, "#define Output%dInterpolation sample\n", psDecl->asOperands[0].ui32RegisterNumber); + bformata(glsl, "#elif IMPORT_%d == %d\n", ui32InterpImp, INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE); + bformata(glsl, "#define Output%dInterpolation noperspective sample\n", psDecl->asOperands[0].ui32RegisterNumber); + bcatcstr(glsl, "#else\n"); + bformata(glsl, "#define Output%dInterpolation \n", psDecl->asOperands[0].ui32RegisterNumber); + bcatcstr(glsl, "#endif\n"); + } + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + if (psShader->eShaderType == VERTEX_SHADER) + { + bformata(glsl, "Output%dInterpolation ", psDecl->asOperands[0].ui32RegisterNumber); + } + + if (InOutSupported(psContext->psShader->eTargetLanguage)) + { + bformata(glsl, "out %s %s%d %s;\n", Precision, type, iNumComponents, OutputName); + } + else + { + bformata(glsl, "varying %s %s%d %s;\n", Precision, type, iNumComponents, OutputName); + } + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + bcstrfree(OutputName); + + break; + } + case GEOMETRY_SHADER: + { + int stream = 0; + char* OutputName = GetDeclaredOutputName(psContext, GEOMETRY_SHADER, psOperand, &stream); + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + bformata(glsl, "out %s4 %s;\n", type, OutputName); + if (stream) + { + bformata(glsl, "#define Output%d_S%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, stream, OutputName); + } + else + { + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + } + bcstrfree(OutputName); + break; + } + case HULL_SHADER: + { + int stream = 0; + char* OutputName = GetDeclaredOutputName(psContext, HULL_SHADER, psOperand, &stream); + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber != 0); //Reg 0 should be gl_out[gl_InvocationID].gl_Position. + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + bformata(glsl, "out %s4 %s[];\n", type, OutputName); + bformata(glsl, "#define Output%d %s[gl_InvocationID]\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + bcstrfree(OutputName); + break; + } + case DOMAIN_SHADER: + { + int stream = 0; + char* OutputName = GetDeclaredOutputName(psContext, DOMAIN_SHADER, psOperand, &stream); + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + bformata(glsl, "out %s4 %s;\n", type, OutputName); + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + bcstrfree(OutputName); + break; + } + } + } + else + { + /* + Multiple outputs can be packed into one register. e.g. + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------- ------ + // FACTOR 0 x 3 NONE int x + // MAX 0 y 3 NONE int y + + We want unique outputs to make it easier to use transform feedback. + + out ivec4 FACTOR0; + #define Output3 FACTOR0 + out ivec4 MAX0; + + MAIN SHADER CODE. Writes factor and max to Output3 which aliases FACTOR0. + + MAX0.x = FACTOR0.y; + + This unpacking of outputs is only done when using HLSLCC_FLAG_INOUT_SEMANTIC_NAMES. + When not set the application will be using HLSL reflection information to discover + what the input and outputs mean if need be. + */ + + // + + if ((psContext->flags & HLSLCC_FLAG_INOUT_SEMANTIC_NAMES) && (psDecl->asOperands[0].eType == OPERAND_TYPE_OUTPUT)) + { + const Operand* psOperand = &psDecl->asOperands[0]; + InOutSignature* psSignature = NULL; + const char* type = "vec"; + int stream = 0; + char* OutputName = GetDeclaredOutputName(psContext, psShader->eShaderType, psOperand, &stream); + + GetOutputSignatureFromRegister(psOperand->ui32RegisterNumber, + psOperand->ui32CompMask, + 0, + &psShader->sInfo, + &psSignature); + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + switch (psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + type = "uvec"; + break; + } + case INOUT_COMPONENT_SINT32: + { + type = "ivec"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + bformata(glsl, "out %s4 %s;\n", type, OutputName); + + psContext->havePostShaderCode[psContext->currentPhase] = 1; + + psContext->currentGLSLString = &psContext->postShaderCode[psContext->currentPhase]; + glsl = *psContext->currentGLSLString; + + bcatcstr(glsl, OutputName); + bcstrfree(OutputName); + AddSwizzleUsingElementCount(psContext, GetNumSwizzleElements(psOperand)); + bformata(glsl, " = Output%d", psOperand->ui32RegisterNumber); + TranslateOperandSwizzle(psContext, psOperand); + bcatcstr(glsl, ";\n"); + + psContext->currentGLSLString = &psContext->glsl; + glsl = *psContext->currentGLSLString; + } + } +} + +void DeclareUBOConstants(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, ConstantBuffer* psCBuf) +{ + bstring glsl = *psContext->currentGLSLString; + + uint32_t i, implicitOffset; + const char* Name = psCBuf->Name; + uint32_t auiSortedVars[MAX_SHADER_VARS]; + if (psCBuf->Name[0] == '$') //For $Globals + { + Name++; + } + + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + PreDeclareStructType(psContext, psCBuf->asVars[i].sType.Name, &psCBuf->asVars[i].sType); + } + + /* [layout (location = X)] uniform vec4 HLSLConstantBufferName[numConsts]; */ + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) && (psContext->flags & HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS) == 0) + { + bformata(glsl, "layout(binding = %d) ", ui32BindingPoint); + } + + bformata(glsl, "uniform "); + ConvertToUniformBufferName(glsl, psContext->psShader, psCBuf->Name); + bformata(glsl, " {\n "); + + if (psCBuf->ui32NumVars > 0) + { + uint32_t bSorted = 1; + auiSortedVars[0] = 0; + for (i = 1; i < psCBuf->ui32NumVars; ++i) + { + auiSortedVars[i] = i; + bSorted = bSorted && psCBuf->asVars[i - 1].ui32StartOffset <= psCBuf->asVars[i].ui32StartOffset; + } + while (!bSorted) + { + bSorted = 1; + for (i = 1; i < psCBuf->ui32NumVars; ++i) + { + if (psCBuf->asVars[auiSortedVars[i - 1]].ui32StartOffset > psCBuf->asVars[auiSortedVars[i]].ui32StartOffset) + { + uint32_t uiTemp = auiSortedVars[i]; + auiSortedVars[i] = auiSortedVars[i - 1]; + auiSortedVars[i - 1] = uiTemp; + bSorted = 0; + } + } + } + } + + implicitOffset = 0; + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + uint32_t uVarAlignment, uVarSize; + ShaderVar* psVar = psCBuf->asVars + auiSortedVars[i]; + GetSTD140Layout(&psVar->sType, &uVarAlignment, &uVarSize); + + if ((implicitOffset + 16 - 1) / 16 < psVar->ui32StartOffset / 16) + { + uint32_t uNumPaddingUvecs = psVar->ui32StartOffset / 16 - (implicitOffset + 16 - 1) / 16; + bcatcstr(glsl, "\tuvec4 padding_"); + ConvertToUniformBufferName(glsl, psContext->psShader, psCBuf->Name); + bformata(glsl, "_%d[%d];\n", implicitOffset, uNumPaddingUvecs); + implicitOffset = psVar->ui32StartOffset - psVar->ui32StartOffset % 16; + } + + if ((implicitOffset + 4 - 1) / 4 < psVar->ui32StartOffset / 4) + { + uint32_t uNumPaddingUints = psVar->ui32StartOffset / 4 - (implicitOffset + 4 - 1) / 4; + uint32_t uPaddingUint; + for (uPaddingUint = 0; uPaddingUint < uNumPaddingUints; ++uPaddingUint) + { + bcatcstr(glsl, "\tuint padding_"); + ConvertToUniformBufferName(glsl, psContext->psShader, psCBuf->Name); + bformata(glsl, "_%d_%d;\n", psVar->ui32StartOffset, uPaddingUint); + } + implicitOffset = psVar->ui32StartOffset - psVar->ui32StartOffset % 4; + } + + implicitOffset += uVarAlignment - 1; + implicitOffset -= implicitOffset % uVarAlignment; + + ASSERT(implicitOffset == psVar->ui32StartOffset); + + DeclareConstBufferShaderVariable(psContext, psVar->sType.Name, &psVar->sType, 0); + implicitOffset += uVarSize; + } + + bcatcstr(glsl, "};\n"); +} + +void DeclareBufferVariable(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, ConstantBuffer* psCBuf, const Operand* psOperand, const uint32_t ui32GloballyCoherentAccess, const ResourceType eResourceType) +{ + const char* Name = psCBuf->Name; + bstring StructName; +#if !defined(NDEBUG) + uint32_t unnamed_struct = strcmp(psCBuf->asVars[0].Name, "$Element") == 0 ? 1 : 0; +#endif + bstring glsl = *psContext->currentGLSLString; + + ASSERT(psCBuf->ui32NumVars == 1); + ASSERT(unnamed_struct); + + StructName = bfromcstr(""); + + //TranslateOperand(psContext, psOperand, TO_FLAG_NAME_ONLY); + if (psOperand->eType == OPERAND_TYPE_RESOURCE && eResourceType == RTYPE_STRUCTURED) + { + bformata(StructName, "StructuredRes%d", psOperand->ui32RegisterNumber); + } + else if (psOperand->eType == OPERAND_TYPE_RESOURCE && eResourceType == RTYPE_UAV_RWBYTEADDRESS) + { + bformata(StructName, "RawRes%d", psOperand->ui32RegisterNumber); + } + else + { + bformata(StructName, "UAV%d", psOperand->ui32RegisterNumber); + } + + PreDeclareStructType(psContext, bstr2cstr(StructName, '\0'), &psCBuf->asVars[0].sType); + + // Add 'std430' layout for storage buffers. + // We don't use a global setting for all buffers because Mali drivers don't like that. + bcatcstr(glsl, "layout(std430"); + + /* [layout (location = X)] uniform vec4 HLSLConstantBufferName[numConsts]; */ + // If storage blocking binding is not supported, then we must set the binding location in the shader. If we don't do it, + // all the storage buffers of the program get assigned the same value (0). + // Unfortunately this could cause binding collisions between different render stages for a storage buffer. + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) && + (!StorageBlockBindingSupported(psContext->psShader->eTargetLanguage) || (psContext->flags & HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS) == 0)) + { + bformata(glsl, ", binding = %d", ui32BindingPoint); + } + + // Close 'layout' + bcatcstr(glsl, ")"); + + if (ui32GloballyCoherentAccess & GLOBALLY_COHERENT_ACCESS) + { + bcatcstr(glsl, "coherent "); + } + + if (eResourceType == RTYPE_STRUCTURED) + { + bcatcstr(glsl, "readonly "); + } + + bcatcstr(glsl, "buffer "); + if (eResourceType == RTYPE_STRUCTURED) + { + ConvertToTextureName(glsl, psContext->psShader, Name, NULL, 0); + } + else + { + ConvertToUAVName(glsl, psContext->psShader, Name); + } + bcatcstr(glsl, " {\n "); + + DeclareConstBufferShaderVariable(psContext, bstr2cstr(StructName, '\0'), &psCBuf->asVars[0].sType, 1); + + bcatcstr(glsl, "};\n"); + + bdestroy(StructName); +} + +void DeclarePLSVariable(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, ConstantBuffer* plsVar, const Operand* psOperand, const uint32_t ui32GloballyCoherentAccess, const ResourceType eResourceType) +{ + (void)psOperand; + (void)ui32GloballyCoherentAccess; + (void)eResourceType; + + const char* Name = plsVar->Name; +#if !defined(NDEBUG) + uint32_t unnamed_struct = strcmp(plsVar->asVars[0].Name, "$Element") == 0 ? 1 : 0; +#endif + bstring glsl = *psContext->currentGLSLString; + + ASSERT(plsVar->ui32NumVars == 1); + ASSERT(unnamed_struct); + + // Define extension + // TODO: if we need more than one PLS var... we can't redefine the extension every time + // Extensions need to be declared before any non-preprocessor symbols. So we put it all the way at the beginning. + bstring ext = bfromcstralloc(1024, "#extension GL_EXT_shader_pixel_local_storage : require\n"); + bconcat(ext, glsl); + bassign(glsl, ext); + bdestroy(ext); + + switch (ui32BindingPoint) + { + case GMEM_PLS_RO_SLOT: + bcatcstr(glsl, "__pixel_local_inEXT PLS_STRUCT_READ_ONLY"); + break; + case GMEM_PLS_WO_SLOT: + bcatcstr(glsl, "__pixel_local_outEXT PLS_STRUCT_WRITE_ONLY"); + break; + case GMEM_PLS_RW_SLOT: + bcatcstr(glsl, "__pixel_localEXT PLS_STRUCT_READ_WRITE"); + break; + default: + ASSERT(0); + } + + bcatcstr(glsl, "\n{\n"); + + ASSERT(plsVar->ui32NumVars == 1); + ASSERT(plsVar->asVars[0].sType.Members != 0); + DeclarePLSStructVars(psContext, plsVar->asVars[0].sType.Name, &plsVar->asVars[0].sType); + + bcatcstr(glsl, "\n} "); + ConvertToUAVName(glsl, psContext->psShader, Name); + bcatcstr(glsl, ";\n\n"); +} + +void DeclareStructConstants(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, ConstantBuffer* psCBuf, const Operand* psOperand) +{ + bstring glsl = *psContext->currentGLSLString; + + uint32_t i; + + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + PreDeclareStructType(psContext, psCBuf->asVars[i].sType.Name, &psCBuf->asVars[i].sType); + } + + /* [layout (location = X)] uniform vec4 HLSLConstantBufferName[numConsts]; */ + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) && (psContext->flags & HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS) == 0) + { + bformata(glsl, "layout(location = %d) ", ui32BindingPoint); + } + bcatcstr(glsl, "uniform struct "); + TranslateOperand(psContext, psOperand, TO_FLAG_DECLARATION_NAME); + + bcatcstr(glsl, "_Type {\n"); + + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + DeclareConstBufferShaderVariable(psContext, psCBuf->asVars[i].sType.Name, &psCBuf->asVars[i].sType, 0); + } + + bcatcstr(glsl, "} "); + + TranslateOperand(psContext, psOperand, TO_FLAG_DECLARATION_NAME); + + bcatcstr(glsl, ";\n"); +} + +void TranslateDeclaration(HLSLCrossCompilerContext* psContext, const Declaration* psDecl) +{ + bstring glsl = *psContext->currentGLSLString; + Shader* psShader = psContext->psShader; + + switch (psDecl->eOpcode) + { + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_PS_SGV: + case OPCODE_DCL_INPUT_PS_SIV: + { + const SPECIAL_NAME eSpecialName = psDecl->asOperands[0].eSpecialName; + switch (eSpecialName) + { + case NAME_POSITION: + { + if (psShader->eShaderType == PIXEL_SHADER) + { + AddBuiltinInput(psContext, psDecl, "gl_FragCoord", 4); + } + else + { + AddBuiltinInput(psContext, psDecl, "gl_Position", 4); + } + break; + } + case NAME_RENDER_TARGET_ARRAY_INDEX: + { + AddBuiltinInput(psContext, psDecl, "gl_Layer", 1); + break; + } + case NAME_CLIP_DISTANCE: + { + AddBuiltinInput(psContext, psDecl, "gl_ClipDistance", 4); + break; + } + case NAME_VIEWPORT_ARRAY_INDEX: + { + AddBuiltinInput(psContext, psDecl, "gl_ViewportIndex", 1); + break; + } + case NAME_INSTANCE_ID: + { + AddBuiltinInput(psContext, psDecl, "uint(gl_InstanceID)", 1); + break; + } + case NAME_IS_FRONT_FACE: + { + /* + Cast to uint used because + if(gl_FrontFacing != 0) failed to compiled on Intel HD 4000. + Suggests no implicit conversion for bool<->uint. + */ + + AddBuiltinInput(psContext, psDecl, "uint(gl_FrontFacing)", 1); + break; + } + case NAME_SAMPLE_INDEX: + { + AddBuiltinInput(psContext, psDecl, "gl_SampleID", 1); + break; + } + case NAME_VERTEX_ID: + { + AddBuiltinInput(psContext, psDecl, "uint(gl_VertexID)", 1); + break; + } + case NAME_PRIMITIVE_ID: + { + AddBuiltinInput(psContext, psDecl, "gl_PrimitiveID", 1); + break; + } + default: + { + bformata(glsl, "in vec4 %s;\n", psDecl->asOperands[0].pszSpecialName); + + bcatcstr(glsl, "#define "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, " %s\n", psDecl->asOperands[0].pszSpecialName); + break; + } + } + break; + } + + case OPCODE_DCL_OUTPUT_SIV: + { + switch (psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT4, 0, "gl_Position"); + break; + } + case NAME_RENDER_TARGET_ARRAY_INDEX: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_INT, 0, "gl_Layer"); + break; + } + case NAME_CLIP_DISTANCE: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_ClipDistance"); + break; + } + case NAME_VIEWPORT_ARRAY_INDEX: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_INT, 0, "gl_ViewportIndex"); + break; + } + case NAME_VERTEX_ID: + { + ASSERT(0); //VertexID is not an output + break; + } + case NAME_PRIMITIVE_ID: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_INT, 0, "gl_PrimitiveID"); + break; + } + case NAME_INSTANCE_ID: + { + ASSERT(0); //InstanceID is not an output + break; + } + case NAME_IS_FRONT_FACE: + { + ASSERT(0); //FrontFacing is not an output + break; + } + case NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR: + { + if (psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 4, "gl_TessLevelOuter"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[0]"); + } + break; + } + case NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[1]"); + break; + } + case NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[2]"); + break; + } + case NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[3]"); + break; + } + case NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR: + { + if (psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 3, "gl_TessLevelOuter"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[0]"); + } + break; + } + case NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[1]"); + break; + } + case NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[2]"); + break; + } + case NAME_FINAL_LINE_DENSITY_TESSFACTOR: + { + if (psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 2, "gl_TessLevelOuter"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[0]"); + } + break; + } + case NAME_FINAL_LINE_DETAIL_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[1]"); + break; + } + case NAME_FINAL_TRI_INSIDE_TESSFACTOR: + case NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR: + { + if (psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 2, "gl_TessLevelInner"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelInner[0]"); + } + break; + } + case NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelInner[1]"); + break; + } + default: + { + bformata(glsl, "out vec4 %s;\n", psDecl->asOperands[0].pszSpecialName); + + bcatcstr(glsl, "#define "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, " %s\n", psDecl->asOperands[0].pszSpecialName); + break; + } + } + break; + } + case OPCODE_DCL_INPUT: + { + const Operand* psOperand = &psDecl->asOperands[0]; + //Force the number of components to be 4. + /*dcl_output o3.xy + dcl_output o3.z + + Would generate a vec2 and a vec3. We discard the second one making .z invalid! + + */ + int iNumComponents = 4; //GetMaxComponentFromComponentMask(psOperand); + const char* StorageQualifier = "attribute"; + char* InputName; + const char* Precision = ""; + + if ((psOperand->eType == OPERAND_TYPE_INPUT_DOMAIN_POINT) || + (psOperand->eType == OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID) || + (psOperand->eType == OPERAND_TYPE_INPUT_COVERAGE_MASK) || + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID) || + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_GROUP_ID) || + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP) || + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED)) + { + break; + } + + //Already declared as part of an array. + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + { + break; + } + + InputName = GetDeclaredInputName(psContext, psShader->eShaderType, psOperand); + + if (InOutSupported(psContext->psShader->eTargetLanguage)) + { + StorageQualifier = "in"; + } + + if (HavePrecisionQualifers(psShader->eTargetLanguage)) + { + switch (psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + { + Precision = "highp"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_2_8: + { + Precision = "lowp"; + break; + } + case OPERAND_MIN_PRECISION_SINT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_UINT_16: + { + Precision = "mediump"; + break; + } + } + } + + DeclareInput(psContext, psDecl, + "", StorageQualifier, Precision, iNumComponents, (OPERAND_INDEX_DIMENSION)psOperand->iIndexDims, InputName); + bcstrfree(InputName); + + break; + } + case OPCODE_DCL_INPUT_SIV: + { + if (psShader->eShaderType == PIXEL_SHADER) + { + psShader->sInfo.aePixelInputInterpolation[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.eInterpolation; + } + break; + } + case OPCODE_DCL_INPUT_PS: + { + const Operand* psOperand = &psDecl->asOperands[0]; + int iNumComponents = 4; //GetMaxComponentFromComponentMask(psOperand); + const char* StorageQualifier = "varying"; + const char* Precision = ""; + char* InputName = GetDeclaredInputName(psContext, PIXEL_SHADER, psOperand); + const char* Interpolation = ""; + + //Already declared as part of an array. + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + { + break; + } + + if (InOutSupported(psContext->psShader->eTargetLanguage)) + { + StorageQualifier = "in"; + } + + switch (psDecl->value.eInterpolation) + { + case INTERPOLATION_CONSTANT: + { + Interpolation = "flat"; + break; + } + case INTERPOLATION_LINEAR: + { + break; + } + case INTERPOLATION_LINEAR_CENTROID: + { + Interpolation = "centroid"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE: + { + Interpolation = "noperspective"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID: + { + Interpolation = "noperspective centroid"; + break; + } + case INTERPOLATION_LINEAR_SAMPLE: + { + Interpolation = "sample"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE: + { + Interpolation = "noperspective sample"; + break; + } + } + + if (HavePrecisionQualifers(psShader->eTargetLanguage)) + { + switch (psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + { + Precision = "highp"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_2_8: + { + Precision = "lowp"; + break; + } + case OPERAND_MIN_PRECISION_SINT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_UINT_16: + { + Precision = "mediump"; + break; + } + } + } + + DeclareInput(psContext, psDecl, + Interpolation, StorageQualifier, Precision, iNumComponents, INDEX_1D, InputName); + bcstrfree(InputName); + + break; + } + case OPCODE_DCL_TEMPS: + { + const uint32_t ui32NumTemps = psDecl->value.ui32NumTemps; + + if (psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING && psContext->psShader->eShaderType != HULL_SHADER) + { + break; + } + + if (ui32NumTemps > 0) + { + bformata(glsl, "vec4 Temp[%d];\n", ui32NumTemps); + if (psContext->psShader->bUseTempCopy) + { + bcatcstr(glsl, "vec4 TempCopy;\n"); + } + + bformata(glsl, "ivec4 Temp_int[%d];\n", ui32NumTemps); + if (psContext->psShader->bUseTempCopy) + { + bcatcstr(glsl, "vec4 TempCopy_int;\n"); + } + if (HaveUVec(psShader->eTargetLanguage)) + { + bformata(glsl, "uvec4 Temp_uint[%d];\n", ui32NumTemps); + if (psContext->psShader->bUseTempCopy) + { + bcatcstr(glsl, "uvec4 TempCopy_uint;\n"); + } + } + if (psShader->fp64) + { + bformata(glsl, "dvec4 Temp_double[%d];\n", ui32NumTemps); + if (psContext->psShader->bUseTempCopy) + { + bcatcstr(glsl, "dvec4 TempCopy_double;\n"); + } + } + } + + break; + } + case OPCODE_SPECIAL_DCL_IMMCONST: + { + const Operand* psDest = &psDecl->asOperands[0]; + const Operand* psSrc = &psDecl->asOperands[1]; + + ASSERT(psSrc->eType == OPERAND_TYPE_IMMEDIATE32); + if (psDest->eType == OPERAND_TYPE_SPECIAL_IMMCONSTINT) + { + bformata(glsl, "const ivec4 IntImmConst%d = ", psDest->ui32RegisterNumber); + } + else + { + bformata(glsl, "const vec4 ImmConst%d = ", psDest->ui32RegisterNumber); + AddToDx9ImmConstIndexableArray(psContext, psDest); + } + TranslateOperand(psContext, psSrc, TO_FLAG_NONE); + bcatcstr(glsl, ";\n"); + + break; + } + case OPCODE_DCL_CONSTANT_BUFFER: + { + const Operand* psOperand = &psDecl->asOperands[0]; + const uint32_t ui32BindingPoint = psOperand->aui32ArraySizes[0]; + + ConstantBuffer* psCBuf = NULL; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, ui32BindingPoint, &psContext->psShader->sInfo, &psCBuf); + + if (psCBuf) + { + // Constant buffers declared as "dynamicIndexed" are declared as raw vec4 arrays, as there is no general way to retrieve the member corresponding to a dynamic index. + // Simple cases can probably be handled easily, but for example when arrays (possibly nested with structs) are contained in the constant buffer and the shader reads + // from a dynamic index we would need to "undo" the operations done in order to compute the variable offset, and such a feature is not available at the moment. + psCBuf->blob = psDecl->value.eCBAccessPattern == CONSTANT_BUFFER_ACCESS_PATTERN_DYNAMICINDEXED; + } + + // We don't have a original resource name, maybe generate one??? + if (!psCBuf) + { + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) && (psContext->flags & HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS) == 0) + { + bformata(glsl, "layout(location = %d) ", ui32BindingPoint); + } + + bformata(glsl, "layout(std140) uniform ConstantBuffer%d {\n\tvec4 data[%d];\n} cb%d;\n", ui32BindingPoint, psOperand->aui32ArraySizes[1], ui32BindingPoint); + break; + } + else if (psCBuf->blob) + { + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) && (psContext->flags & HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS) == 0) + { + bformata(glsl, "layout(location = %d) ", ui32BindingPoint); + } + + bcatcstr(glsl, "layout(std140) uniform "); + ConvertToUniformBufferName(glsl, psShader, psCBuf->Name); + bcatcstr(glsl, " {\n\tvec4 "); + ConvertToUniformBufferName(glsl, psShader, psCBuf->Name); + bformata(glsl, "_data[%d];\n};\n", psOperand->aui32ArraySizes[1]); + break; + } + + if (psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) + { + if (psContext->flags & HLSLCC_FLAG_GLOBAL_CONSTS_NEVER_IN_UBO && psCBuf->Name[0] == '$') + { + DeclareStructConstants(psContext, ui32BindingPoint, psCBuf, psOperand); + } + else + { + DeclareUBOConstants(psContext, ui32BindingPoint, psCBuf); + } + } + else + { + DeclareStructConstants(psContext, ui32BindingPoint, psCBuf, psOperand); + } + break; + } + case OPCODE_DCL_RESOURCE: + { + bool isGmemResource = false; + const int initialMemSize = 64; + bstring earlyMain = bfromcstralloc(initialMemSize, ""); + if (IsGmemReservedSlot(FBF_EXT_COLOR, psDecl->asOperands[0].ui32RegisterNumber)) + { + // A GMEM reserve slot was used. + // This is not a resource but an inout RT of the pixel shader + int regNum = GetGmemInputResourceSlot(psDecl->asOperands[0].ui32RegisterNumber); + // FXC thinks this is a texture so we can't trust the number of elements. We get that from the "register number". + int numElements = GetGmemInputResourceNumElements(psDecl->asOperands[0].ui32RegisterNumber); + ASSERT(numElements); + + const char* Precision = "highp"; + const char* outputName = "PixOutput"; + + bformata(glsl, "layout(location = %d) ", regNum); + bformata(glsl, "inout %s vec%d %s%d;\n", Precision, numElements, outputName, regNum); + + const char* mask[] = { "x", "y", "z", "w" }; + // Since we are using Textures as GMEM inputs FXC will threat them as vec4 values. The rendertarget may not be a vec4 (numElements != 4) + // so we create a new variable (GMEM_InputXX) at the beginning of the shader that wraps the rendertarget value. + bformata(earlyMain, "%s vec4 GMEM_Input%d = %s vec4(%s%d.", Precision, regNum, Precision, outputName, regNum); + for (int i = 0; i < 4; ++i) + { + bformata(earlyMain, "%s", i < numElements ? mask[i] : mask[numElements - 1]); + } + bcatcstr(earlyMain, ");\n"); + isGmemResource = true; + } + else if (IsGmemReservedSlot(FBF_ARM_COLOR, psDecl->asOperands[0].ui32RegisterNumber)) + { + bcatcstr(earlyMain, "vec4 GMEM_Input0 = vec4(gl_LastFragColorARM);\n"); + isGmemResource = true; + } + else if (IsGmemReservedSlot(FBF_ARM_DEPTH, psDecl->asOperands[0].ui32RegisterNumber)) + { + bcatcstr(earlyMain, "vec4 GMEM_Depth = vec4(gl_LastFragDepthARM);\n"); + isGmemResource = true; + } + else if (IsGmemReservedSlot(FBF_ARM_STENCIL, psDecl->asOperands[0].ui32RegisterNumber)) + { + bcatcstr(earlyMain, "ivec4 GMEM_Stencil = ivec4(gl_LastFragStencilARM);\n"); + isGmemResource = true; + } + + if (isGmemResource) + { + if (earlyMain->slen) + { + bstring* savedStringPtr = psContext->currentGLSLString; + psContext->currentGLSLString = &psContext->earlyMain; + psContext->indent++; + AddIndentation(psContext); + bconcat(*psContext->currentGLSLString, earlyMain); + psContext->indent--; + psContext->currentGLSLString = savedStringPtr; + } + break; + } + + char* szResourceTypeName = ""; + uint32_t bCanBeCompare; + uint32_t i; + SamplerMask sMask; + + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions) && (psContext->flags & HLSLCC_FLAG_AVOID_RESOURCE_BINDINGS_AND_LOCATIONS) == 0) + { + //Constant buffer locations start at 0. Resource locations start at ui32NumConstantBuffers. + bformata(glsl, "layout(location = %d) ", psContext->psShader->sInfo.ui32NumConstantBuffers + psDecl->asOperands[0].ui32RegisterNumber); + } + + switch (psDecl->value.eResourceDimension) + { + case RESOURCE_DIMENSION_BUFFER: + szResourceTypeName = "Buffer"; + bCanBeCompare = 0; + break; + case RESOURCE_DIMENSION_TEXTURE1D: + szResourceTypeName = "1D"; + bCanBeCompare = 1; + break; + case RESOURCE_DIMENSION_TEXTURE2D: + szResourceTypeName = "2D"; + bCanBeCompare = 1; + break; + case RESOURCE_DIMENSION_TEXTURE2DMS: + szResourceTypeName = "2DMS"; + bCanBeCompare = 0; + break; + case RESOURCE_DIMENSION_TEXTURE3D: + szResourceTypeName = "3D"; + bCanBeCompare = 0; + break; + case RESOURCE_DIMENSION_TEXTURECUBE: + szResourceTypeName = "Cube"; + bCanBeCompare = 1; + break; + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + szResourceTypeName = "1DArray"; + bCanBeCompare = 1; + break; + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + szResourceTypeName = "2DArray"; + bCanBeCompare = 1; + break; + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + szResourceTypeName = "2DMSArray"; + bCanBeCompare = 0; + break; + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + szResourceTypeName = "CubeArray"; + bCanBeCompare = 1; + break; + } + + for (i = 0; i < psShader->sInfo.ui32NumSamplers; ++i) + { + if (psShader->sInfo.asSamplers[i].sMask.ui10TextureBindPoint == psDecl->asOperands[0].ui32RegisterNumber) + { + sMask = psShader->sInfo.asSamplers[i].sMask; + + if (bCanBeCompare && sMask.bCompareSample) // Sampled with depth comparison + { + bformata(glsl, "uniform sampler%sShadow ", szResourceTypeName); + TextureName(*psContext->currentGLSLString, psContext->psShader, psDecl->asOperands[0].ui32RegisterNumber, sMask.ui10SamplerBindPoint, 1); + bcatcstr(glsl, ";\n"); + } + if (sMask.bNormalSample || !sMask.bCompareSample) // Either sampled normally or with texelFetch + { + if (psDecl->ui32TexReturnType == RETURN_TYPE_SINT) + { + bformata(glsl, "uniform isampler%s ", szResourceTypeName); + } + else if (psDecl->ui32TexReturnType == RETURN_TYPE_UINT) + { + bformata(glsl, "uniform usampler%s ", szResourceTypeName); + } + else + { + bformata(glsl, "uniform sampler%s ", szResourceTypeName); + } + TextureName(*psContext->currentGLSLString, psContext->psShader, psDecl->asOperands[0].ui32RegisterNumber, sMask.ui10SamplerBindPoint, 0); + bcatcstr(glsl, ";\n"); + } + } + } + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_TEXTURES); + psShader->aeResourceDims[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.eResourceDimension; + break; + } + case OPCODE_DCL_OUTPUT: + { + if (psShader->eShaderType == HULL_SHADER && psDecl->asOperands[0].ui32RegisterNumber == 0) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT4, 0, "gl_out[gl_InvocationID].gl_Position"); + } + else + { + AddUserOutput(psContext, psDecl); + } + break; + } + case OPCODE_DCL_GLOBAL_FLAGS: + { + uint32_t ui32Flags = psDecl->value.ui32GlobalFlags; + + // OpenGL versions lower than 4.1 don't support the + // layout(early_fragment_tests) directive and will fail to compile + // the shader + if (ui32Flags & GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL && EarlyDepthTestSupported(psShader->eTargetLanguage) && + !(psShader->eGmemType & (FBF_ARM_DEPTH | FBF_ARM_STENCIL))) // Early fragment test is not allowed when fetching from the depth/stencil buffer. + { + bcatcstr(glsl, "layout(early_fragment_tests) in;\n"); + } + if (!(ui32Flags & GLOBAL_FLAG_REFACTORING_ALLOWED)) + { + //TODO add precise + //HLSL precise - http://msdn.microsoft.com/en-us/library/windows/desktop/hh447204(v=vs.85).aspx + } + if (ui32Flags & GLOBAL_FLAG_ENABLE_DOUBLE_PRECISION_FLOAT_OPS) + { + bcatcstr(glsl, "#extension GL_ARB_gpu_shader_fp64 : enable\n"); + psShader->fp64 = 1; + } + break; + } + + case OPCODE_DCL_THREAD_GROUP: + { + bformata(glsl, "layout(local_size_x = %d, local_size_y = %d, local_size_z = %d) in;\n", + psDecl->value.aui32WorkGroupSize[0], + psDecl->value.aui32WorkGroupSize[1], + psDecl->value.aui32WorkGroupSize[2]); + break; + } + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + { + if (psContext->psShader->eShaderType == HULL_SHADER) + { + psContext->psShader->sInfo.eTessOutPrim = psDecl->value.eTessOutPrim; + } + break; + } + case OPCODE_DCL_TESS_DOMAIN: + { + if (psContext->psShader->eShaderType == DOMAIN_SHADER) + { + switch (psDecl->value.eTessDomain) + { + case TESSELLATOR_DOMAIN_ISOLINE: + { + bcatcstr(glsl, "layout(isolines) in;\n"); + break; + } + case TESSELLATOR_DOMAIN_TRI: + { + bcatcstr(glsl, "layout(triangles) in;\n"); + break; + } + case TESSELLATOR_DOMAIN_QUAD: + { + bcatcstr(glsl, "layout(quads) in;\n"); + break; + } + default: + { + break; + } + } + } + break; + } + case OPCODE_DCL_TESS_PARTITIONING: + { + if (psContext->psShader->eShaderType == HULL_SHADER) + { + psContext->psShader->sInfo.eTessPartitioning = psDecl->value.eTessPartitioning; + } + break; + } + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + { + switch (psDecl->value.eOutputPrimitiveTopology) + { + case PRIMITIVE_TOPOLOGY_POINTLIST: + { + bcatcstr(glsl, "layout(points) out;\n"); + break; + } + case PRIMITIVE_TOPOLOGY_LINELIST_ADJ: + case PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ: + case PRIMITIVE_TOPOLOGY_LINELIST: + case PRIMITIVE_TOPOLOGY_LINESTRIP: + { + bcatcstr(glsl, "layout(line_strip) out;\n"); + break; + } + + case PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ: + case PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ: + case PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: + case PRIMITIVE_TOPOLOGY_TRIANGLELIST: + { + bcatcstr(glsl, "layout(triangle_strip) out;\n"); + break; + } + default: + { + break; + } + } + break; + } + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + { + bformata(glsl, "layout(max_vertices = %d) out;\n", psDecl->value.ui32MaxOutputVertexCount); + break; + } + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + { + switch (psDecl->value.eInputPrimitive) + { + case PRIMITIVE_POINT: + { + bcatcstr(glsl, "layout(points) in;\n"); + break; + } + case PRIMITIVE_LINE: + { + bcatcstr(glsl, "layout(lines) in;\n"); + break; + } + case PRIMITIVE_LINE_ADJ: + { + bcatcstr(glsl, "layout(lines_adjacency) in;\n"); + break; + } + case PRIMITIVE_TRIANGLE: + { + bcatcstr(glsl, "layout(triangles) in;\n"); + break; + } + case PRIMITIVE_TRIANGLE_ADJ: + { + bcatcstr(glsl, "layout(triangles_adjacency) in;\n"); + break; + } + default: + { + break; + } + } + break; + } + case OPCODE_DCL_INTERFACE: + { + const uint32_t interfaceID = psDecl->value.interface.ui32InterfaceID; + const uint32_t numUniforms = psDecl->value.interface.ui32ArraySize; + const uint32_t ui32NumBodiesPerTable = psContext->psShader->funcPointer[interfaceID].ui32NumBodiesPerTable; + ShaderVar* psVar; + uint32_t varFound; + + const char* uniformName; + + varFound = GetInterfaceVarFromOffset(interfaceID, &psContext->psShader->sInfo, &psVar); + ASSERT(varFound); + uniformName = &psVar->sType.Name[0]; + + bformata(glsl, "subroutine uniform SubroutineType %s[%d*%d];\n", uniformName, numUniforms, ui32NumBodiesPerTable); + break; + } + case OPCODE_DCL_FUNCTION_BODY: + { + //bformata(glsl, "void Func%d();//%d\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].eType); + break; + } + case OPCODE_DCL_FUNCTION_TABLE: + { + break; + } + case OPCODE_CUSTOMDATA: + { + const uint32_t ui32NumVec4 = psDecl->ui32NumOperands; + const uint32_t ui32NumVec4Minus1 = (ui32NumVec4 - 1); + uint32_t ui32ConstIndex = 0; + int integerCoords[4]; + bool qualcommWorkaround = (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) != 0; + + if (qualcommWorkaround) + { + bformata(glsl, "const "); + } + + bformata(glsl, "ivec4 immediateConstBufferInt[%d] = ivec4[%d] (\n", ui32NumVec4, ui32NumVec4); + for (ui32ConstIndex = 0; ui32ConstIndex < ui32NumVec4Minus1; ui32ConstIndex++) + { + integerCoords[0] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + integerCoords[1] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + integerCoords[2] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + integerCoords[3] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + + bformata(glsl, "\tivec4(%d, %d, %d, %d), \n", integerCoords[0], integerCoords[1], integerCoords[2], integerCoords[3]); + } + //No trailing comma on this one + integerCoords[0] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + integerCoords[1] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + integerCoords[2] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + integerCoords[3] = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + + bformata(glsl, "\tivec4(%d, %d, %d, %d)\n", integerCoords[0], integerCoords[1], integerCoords[2], integerCoords[3]); + bcatcstr(glsl, ");\n"); + + //If ShaderBitEncodingSupported then 1 integer buffer, use intBitsToFloat to get float values. - More instructions. + //else 2 buffers - one integer and one float. - More data + + if (ShaderBitEncodingSupported(psShader->eTargetLanguage) == 0) + { + float floatCoords[4]; + bcatcstr(glsl, "#define immediateConstBufferI(idx) immediateConstBufferInt[idx]\n"); + bcatcstr(glsl, "#define immediateConstBufferF(idx) immediateConstBuffer[idx]\n"); + + bformata(glsl, "vec4 immediateConstBuffer[%d] = vec4[%d] (\n", ui32NumVec4, ui32NumVec4); + for (ui32ConstIndex = 0; ui32ConstIndex < ui32NumVec4Minus1; ui32ConstIndex++) + { + floatCoords[0] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + floatCoords[1] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + floatCoords[2] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + floatCoords[3] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + + //A single vec4 can mix integer and float types. + //Forced NAN and INF to zero inside the immediate constant buffer. This will allow the shader to compile. + if (fpcheck(floatCoords[0])) + { + floatCoords[0] = 0; + } + if (fpcheck(floatCoords[1])) + { + floatCoords[1] = 0; + } + if (fpcheck(floatCoords[2])) + { + floatCoords[2] = 0; + } + if (fpcheck(floatCoords[3])) + { + floatCoords[3] = 0; + } + + bformata(glsl, "\tvec4(%e, %e, %e, %e), \n", floatCoords[0], floatCoords[1], floatCoords[2], floatCoords[3]); + } + //No trailing comma on this one + floatCoords[0] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + floatCoords[1] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + floatCoords[2] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + floatCoords[3] = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + if (fpcheck(floatCoords[0])) + { + floatCoords[0] = 0; + } + if (fpcheck(floatCoords[1])) + { + floatCoords[1] = 0; + } + if (fpcheck(floatCoords[2])) + { + floatCoords[2] = 0; + } + if (fpcheck(floatCoords[3])) + { + floatCoords[3] = 0; + } + bformata(glsl, "\tvec4(%e, %e, %e, %e)\n", floatCoords[0], floatCoords[1], floatCoords[2], floatCoords[3]); + bcatcstr(glsl, ");\n"); + } + else + { + if (qualcommWorkaround) + { + bcatcstr(glsl, "ivec4 immediateConstBufferI(int idx) { return immediateConstBufferInt[idx]; }\n"); + bcatcstr(glsl, "vec4 immediateConstBufferF(int idx) { return intBitsToFloat(immediateConstBufferInt[idx]); }\n"); + } + else + { + bcatcstr(glsl, "#define immediateConstBufferI(idx) immediateConstBufferInt[idx]\n"); + bcatcstr(glsl, "#define immediateConstBufferF(idx) intBitsToFloat(immediateConstBufferInt[idx])\n"); + } + } + + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + const uint32_t forkPhaseNum = psDecl->value.aui32HullPhaseInstanceInfo[0]; + const uint32_t instanceCount = psDecl->value.aui32HullPhaseInstanceInfo[1]; + bformata(glsl, "const int HullPhase%dInstanceCount = %d;\n", forkPhaseNum, instanceCount); + break; + } + case OPCODE_DCL_INDEXABLE_TEMP: + { + const uint32_t ui32RegIndex = psDecl->sIdxTemp.ui32RegIndex; + const uint32_t ui32RegCount = psDecl->sIdxTemp.ui32RegCount; + const uint32_t ui32RegComponentSize = psDecl->sIdxTemp.ui32RegComponentSize; + bformata(glsl, "vec%d TempArray%d[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + bformata(glsl, "ivec%d TempArray%d_int[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + if (HaveUVec(psShader->eTargetLanguage)) + { + bformata(glsl, "uvec%d TempArray%d_uint[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + } + if (psShader->fp64) + { + bformata(glsl, "dvec%d TempArray%d_double[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + } + break; + } + case OPCODE_DCL_INDEX_RANGE: + { + break; + } + case OPCODE_HS_DECLS: + { + break; + } + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + { + break; + } + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + { + if (psContext->psShader->eShaderType == HULL_SHADER) + { + bformata(glsl, "layout(vertices=%d) out;\n", psDecl->value.ui32MaxOutputVertexCount); + } + break; + } + case OPCODE_HS_FORK_PHASE: + { + break; + } + case OPCODE_HS_JOIN_PHASE: + { + break; + } + case OPCODE_DCL_SAMPLER: + { + break; + } + case OPCODE_DCL_HS_MAX_TESSFACTOR: + { + //For GLSL the max tessellation factor is fixed to the value of gl_MaxTessGenLevel. + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + { + if (psDecl->sUAV.ui32GloballyCoherentAccess & GLOBALLY_COHERENT_ACCESS) + { + bcatcstr(glsl, "coherent "); + } + + if (psShader->aiOpcodeUsed[OPCODE_LD_UAV_TYPED] == 0) + { + bcatcstr(glsl, "writeonly "); + } + else + { + if (psShader->aiOpcodeUsed[OPCODE_STORE_UAV_TYPED] == 0) + { + bcatcstr(glsl, "readonly "); + } + + switch (psDecl->sUAV.Type) + { + case RETURN_TYPE_FLOAT: + bcatcstr(glsl, "layout(rgba32f) "); + break; + case RETURN_TYPE_UNORM: + bcatcstr(glsl, "layout(rgba8) "); + break; + case RETURN_TYPE_SNORM: + bcatcstr(glsl, "layout(rgba8_snorm) "); + break; + case RETURN_TYPE_UINT: + bcatcstr(glsl, "layout(rgba32ui) "); + break; + case RETURN_TYPE_SINT: + bcatcstr(glsl, "layout(rgba32i) "); + break; + default: + ASSERT(0); + } + } + + { + char* prefix = ""; + switch (psDecl->sUAV.Type) + { + case RETURN_TYPE_UINT: + prefix = "u"; + break; + case RETURN_TYPE_SINT: + prefix = "i"; + break; + default: + break; + } + + switch (psDecl->value.eResourceDimension) + { + case RESOURCE_DIMENSION_BUFFER: + bformata(glsl, "uniform %simageBuffer ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE1D: + bformata(glsl, "uniform %simage1D ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE2D: + bformata(glsl, "uniform %simage2D ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE2DMS: + bformata(glsl, "uniform %simage2DMS ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE3D: + bformata(glsl, "uniform %simage3D ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURECUBE: + bformata(glsl, "uniform %simageCube ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + bformata(glsl, "uniform %simage1DArray ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + bformata(glsl, "uniform %simage2DArray ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + bformata(glsl, "uniform %simage3DArray ", prefix); + break; + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + bformata(glsl, "uniform %simageCubeArray ", prefix); + break; + } + } + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + { + const uint32_t ui32BindingPoint = psDecl->asOperands[0].aui32ArraySizes[0]; + ConstantBuffer* psCBuf = NULL; + + if (psDecl->sUAV.bCounter) + { + bformata(glsl, "layout (binding = 1) uniform atomic_uint UAV%d_counter;\n", psDecl->asOperands[0].ui32RegisterNumber); + } + + GetConstantBufferFromBindingPoint(RGROUP_UAV, ui32BindingPoint, &psContext->psShader->sInfo, &psCBuf); + + if (ui32BindingPoint >= GMEM_PLS_RO_SLOT && ui32BindingPoint <= GMEM_PLS_RW_SLOT) + { + DeclarePLSVariable(psContext, ui32BindingPoint, psCBuf, &psDecl->asOperands[0], psDecl->sUAV.ui32GloballyCoherentAccess, RTYPE_UAV_RWSTRUCTURED); + } + else + { + DeclareBufferVariable(psContext, ui32BindingPoint, psCBuf, &psDecl->asOperands[0], psDecl->sUAV.ui32GloballyCoherentAccess, RTYPE_UAV_RWSTRUCTURED); + } + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + { + bstring varName; + if (psDecl->sUAV.bCounter) + { + bformata(glsl, "layout (binding = 1) uniform atomic_uint UAV%d_counter;\n", psDecl->asOperands[0].ui32RegisterNumber); + } + + varName = bfromcstralloc(16, ""); + bformata(varName, "UAV%d", psDecl->asOperands[0].ui32RegisterNumber); + + bformata(glsl, "buffer Block%d {\n\tuint ", psDecl->asOperands[0].ui32RegisterNumber); + ShaderVarName(glsl, psShader, bstr2cstr(varName, '\0')); + bcatcstr(glsl, "[];\n};\n"); + + bdestroy(varName); + break; + } + case OPCODE_DCL_RESOURCE_STRUCTURED: + { + ConstantBuffer* psCBuf = NULL; + + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + DeclareBufferVariable(psContext, psDecl->asOperands[0].ui32RegisterNumber, psCBuf, &psDecl->asOperands[0], 0, RTYPE_STRUCTURED); + break; + } + case OPCODE_DCL_RESOURCE_RAW: + { + bstring varName = bfromcstralloc(16, ""); + bformata(varName, "RawRes%d", psDecl->asOperands[0].ui32RegisterNumber); + + bformata(glsl, "buffer Block%d {\n\tuint ", psDecl->asOperands[0].ui32RegisterNumber); + ShaderVarName(glsl, psContext->psShader, bstr2cstr(varName, '\0')); + bcatcstr(glsl, "[];\n};\n"); + + bdestroy(varName); + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW: + { + ShaderVarType* psVarType = &psShader->sGroupSharedVarType[psDecl->asOperands[0].ui32RegisterNumber]; + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_GROUPSHARED); + ASSERT(psDecl->sTGSM.ui32Count == 1); + + bcatcstr(glsl, "shared uint "); + + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, "[%d];\n", psDecl->sTGSM.ui32Count); + + memset(psVarType, 0, sizeof(ShaderVarType)); + strcpy(psVarType->Name, "$Element"); + + psVarType->Columns = psDecl->sTGSM.ui32Stride / 4; + psVarType->Elements = psDecl->sTGSM.ui32Count; + psVarType->Type = SVT_UINT; + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + { + ShaderVarType* psVarType = &psShader->sGroupSharedVarType[psDecl->asOperands[0].ui32RegisterNumber]; + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_GROUPSHARED); + + bcatcstr(glsl, "shared struct {\n"); + bformata(glsl, "uint value[%d];\n", psDecl->sTGSM.ui32Stride / 4); + bcatcstr(glsl, "} "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, "[%d];\n", + psDecl->sTGSM.ui32Count); + + memset(psVarType, 0, sizeof(ShaderVarType)); + strcpy(psVarType->Name, "$Element"); + + psVarType->Columns = psDecl->sTGSM.ui32Stride / 4; + psVarType->Elements = psDecl->sTGSM.ui32Count; + psVarType->Type = SVT_UINT; + break; + } + case OPCODE_DCL_STREAM: + { + ASSERT(psDecl->asOperands[0].eType == OPERAND_TYPE_STREAM); + + psShader->ui32CurrentVertexOutputStream = psDecl->asOperands[0].ui32RegisterNumber; + + bformata(glsl, "layout(stream = %d) out;\n", psShader->ui32CurrentVertexOutputStream); + + break; + } + case OPCODE_DCL_GS_INSTANCE_COUNT: + { + bformata(glsl, "layout(invocations = %d) in;\n", psDecl->value.ui32GSInstanceCount); + break; + } + default: + { + ASSERT(0); + break; + } + } +} + +//Convert from per-phase temps to global temps for GLSL. +void ConsolidateHullTempVars(Shader* psShader) +{ + uint32_t i, k; + const uint32_t ui32NumDeclLists = 3 + psShader->ui32ForkPhaseCount; + Declaration* pasDeclArray[3 + MAX_FORK_PHASES]; + uint32_t aui32DeclCounts[3 + MAX_FORK_PHASES]; + uint32_t ui32NumTemps = 0; + + i = 0; + + pasDeclArray[i] = psShader->psHSDecl; + aui32DeclCounts[i++] = psShader->ui32HSDeclCount; + + pasDeclArray[i] = psShader->psHSControlPointPhaseDecl; + aui32DeclCounts[i++] = psShader->ui32HSControlPointDeclCount; + for (k = 0; k < psShader->ui32ForkPhaseCount; ++k) + { + pasDeclArray[i] = psShader->apsHSForkPhaseDecl[k]; + aui32DeclCounts[i++] = psShader->aui32HSForkDeclCount[k]; + } + pasDeclArray[i] = psShader->psHSJoinPhaseDecl; + aui32DeclCounts[i++] = psShader->ui32HSJoinDeclCount; + + for (k = 0; k < ui32NumDeclLists; ++k) + { + for (i = 0; i < aui32DeclCounts[k]; ++i) + { + Declaration* psDecl = pasDeclArray[k] + i; + + if (psDecl->eOpcode == OPCODE_DCL_TEMPS) + { + if (ui32NumTemps < psDecl->value.ui32NumTemps) + { + //Find the total max number of temps needed by the entire + //shader. + ui32NumTemps = psDecl->value.ui32NumTemps; + } + //Only want one global temp declaration. + psDecl->value.ui32NumTemps = 0; + } + } + } + + //Find the first temp declaration and make it + //declare the max needed amount of temps. + for (k = 0; k < ui32NumDeclLists; ++k) + { + for (i = 0; i < aui32DeclCounts[k]; ++i) + { + Declaration* psDecl = pasDeclArray[k] + i; + + if (psDecl->eOpcode == OPCODE_DCL_TEMPS) + { + psDecl->value.ui32NumTemps = ui32NumTemps; + return; + } + } + } +} + +const char* GetMangleSuffix(const SHADER_TYPE eShaderType) +{ + switch (eShaderType) + { + case VERTEX_SHADER: + return "VS"; + case PIXEL_SHADER: + return "PS"; + case GEOMETRY_SHADER: + return "GS"; + case HULL_SHADER: + return "HS"; + case DOMAIN_SHADER: + return "DS"; + case COMPUTE_SHADER: + return "CS"; + } + ASSERT(0); + return ""; +} + diff --git a/Code/Tools/HLSLCrossCompiler/src/toGLSLInstruction.c b/Code/Tools/HLSLCrossCompiler/src/toGLSLInstruction.c new file mode 100644 index 0000000000..e5124b4122 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/toGLSLInstruction.c @@ -0,0 +1,5598 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toGLSLInstruction.h" +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/languages.h" +#include "internal_includes/hlslccToolkit.h" +#include "bstrlib.h" +#include "stdio.h" +#include "internal_includes/debug.h" + +#include + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); +extern void WriteEndTrace(HLSLCrossCompilerContext* psContext); + +typedef enum +{ + CMP_EQ, + CMP_LT, + CMP_GE, + CMP_NE, +} ComparisonType; + +void BeginAssignmentEx(HLSLCrossCompilerContext* psContext, const Operand* psDestOperand, uint32_t uSrcToFlag, uint32_t bSaturate, const char* szDestSwizzle) +{ + if (psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING && psContext->psShader->eShaderType != HULL_SHADER) + { + const char* szCastFunction = ""; + SHADER_VARIABLE_TYPE eSrcType; + SHADER_VARIABLE_TYPE eDestType = GetOperandDataType(psContext, psDestOperand); + uint32_t uDestElemCount = GetNumSwizzleElements(psDestOperand); + + eSrcType = TypeFlagsToSVTType(uSrcToFlag); + if (bSaturate) + { + eSrcType = SVT_FLOAT; + } + + if (!DoAssignmentDataTypesMatch(eDestType, eSrcType)) + { + switch (eDestType) + { + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + { + switch (eSrcType) + { + case SVT_UINT: + case SVT_UINT16: + szCastFunction = GetConstructorForTypeGLSL(psContext, eDestType, uDestElemCount, false); + break; + case SVT_FLOAT: + szCastFunction = "floatBitsToInt"; + break; + default: + // Bitcasts from lower precisions floats are ambiguous + ASSERT(0); + break; + } + } + break; + case SVT_UINT: + case SVT_UINT16: + { + switch (eSrcType) + { + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + szCastFunction = GetConstructorForTypeGLSL(psContext, eDestType, uDestElemCount, false); + break; + case SVT_FLOAT: + szCastFunction = "floatBitsToUint"; + break; + default: + // Bitcasts from lower precisions floats are ambiguous + ASSERT(0); + break; + } + } + break; + case SVT_FLOAT: + case SVT_FLOAT10: + case SVT_FLOAT16: + { + switch (eSrcType) + { + case SVT_UINT: + szCastFunction = "uintBitsToFloat"; + break; + case SVT_INT: + szCastFunction = "intBitsToFloat"; + break; + default: + // Bitcasts from lower precisions int/uint are ambiguous + ASSERT(0); + break; + } + } + break; + default: + ASSERT(0); + break; + } + } + + TranslateOperand(psContext, psDestOperand, TO_FLAG_DESTINATION); + if (szDestSwizzle) + { + bformata(*psContext->currentGLSLString, ".%s = %s(", szDestSwizzle, szCastFunction); + } + else + { + bformata(*psContext->currentGLSLString, " = %s(", szCastFunction); + } + } + else + { + TranslateOperand(psContext, psDestOperand, TO_FLAG_DESTINATION | uSrcToFlag); + if (szDestSwizzle) + { + bformata(*psContext->currentGLSLString, ".%s = ", szDestSwizzle); + } + else + { + bcatcstr(*psContext->currentGLSLString, " = "); + } + } + if (bSaturate) + { + bcatcstr(*psContext->currentGLSLString, "clamp("); + } +} + +void BeginAssignment(HLSLCrossCompilerContext* psContext, const Operand* psDestOperand, uint32_t uSrcToFlag, uint32_t bSaturate) +{ + BeginAssignmentEx(psContext, psDestOperand, uSrcToFlag, bSaturate, NULL); +} + +void EndAssignment(HLSLCrossCompilerContext* psContext, const Operand* psDestOperand, uint32_t uSrcToFlag, uint32_t bSaturate) +{ + (void)psDestOperand; + (void)uSrcToFlag; + + if (bSaturate) + { + bcatcstr(*psContext->currentGLSLString, ", 0.0, 1.0)"); + } + + if (psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING && psContext->psShader->eShaderType != HULL_SHADER) + { + bcatcstr(*psContext->currentGLSLString, ")"); + } +} + +static void AddComparision(HLSLCrossCompilerContext* psContext, Instruction* psInst, ComparisonType eType, + uint32_t typeFlag) +{ + bstring glsl = *psContext->currentGLSLString; + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + const uint32_t s1ElemCount = GetNumSwizzleElements(&psInst->asOperands[2]); + + uint32_t minElemCount = destElemCount < s0ElemCount ? destElemCount : s0ElemCount; + + minElemCount = s1ElemCount < minElemCount ? s1ElemCount : minElemCount; + + if (typeFlag == TO_FLAG_NONE) + { + const SHADER_VARIABLE_TYPE e0Type = GetOperandDataType(psContext, &psInst->asOperands[1]); + const SHADER_VARIABLE_TYPE e1Type = GetOperandDataType(psContext, &psInst->asOperands[2]); + if (e0Type != e1Type) + { + typeFlag = TO_FLAG_INTEGER; + } + else + { + switch (e0Type) + { + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + typeFlag = TO_FLAG_INTEGER; + break; + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + typeFlag = TO_FLAG_UNSIGNED_INTEGER; + break; + default: + typeFlag = TO_FLAG_FLOAT; + } + } + } + + if (destElemCount > 1) + { + const char* glslOpcode [] = { + "equal", + "lessThan", + "greaterThanEqual", + "notEqual", + }; + char* constructor = "vec"; + + if (typeFlag & TO_FLAG_INTEGER) + { + constructor = "ivec"; + } + else if (typeFlag & TO_FLAG_UNSIGNED_INTEGER) + { + constructor = "uvec"; + } + + bstring varName = bfromcstr(GetAuxArgumentName(SVT_UINT)); + bcatcstr(varName, "1"); + + //Component-wise compare + AddIndentation(psContext); + if (psContext->psShader->ui32MajorVersion < 4) + { + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + } + else + { + // Qualcomm driver workaround. Save the operation result into + // a temporary variable before assigning it to the register. + bconcat(glsl, varName); + AddSwizzleUsingElementCount(psContext, minElemCount); + bcatcstr(glsl, " = "); + } + + bformata(glsl, "uvec%d(%s(%s4(", minElemCount, glslOpcode[eType], constructor); + TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + //AddSwizzleUsingElementCount(psContext, minElemCount); + bformata(glsl, ", %s4(", constructor); + TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + //AddSwizzleUsingElementCount(psContext, minElemCount); + if (psContext->psShader->ui32MajorVersion < 4) + { + //Result is 1.0f or 0.0f + bcatcstr(glsl, "))"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + } + else + { + bcatcstr(glsl, ")) * 0xFFFFFFFFu;\n"); + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bconcat(glsl, varName); + AddSwizzleUsingElementCount(psContext, minElemCount); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + } + bcatcstr(glsl, ";\n"); + } + else + { + const char* glslOpcode [] = { + "==", + "<", + ">=", + "!=", + }; + + bool qualcommWorkaround = (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) != 0; + const char* tempVariableName = "cond"; + //Scalar compare + AddIndentation(psContext); + // There's a bug with Qualcomm OpenGLES 3.0 drivers that + // makes something like this: "temp1.x = temp2.x == 0 ? 1.0f : 0.0f" always return 0.0f + // The workaround is saving the result in a temp variable: bool cond = temp2.x == 0; temp1.x = !!cond ? 1.0f : 0.0f + if (qualcommWorkaround) + { + bcatcstr(glsl, "{\n"); + ++psContext->indent; + AddIndentation(psContext); + bformata(glsl, "bool %s = ", tempVariableName); + bcatcstr(glsl, "("); + TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + bcatcstr(glsl, ")"); + if (s0ElemCount > minElemCount) + { + AddSwizzleUsingElementCount(psContext, minElemCount); + } + bformata(glsl, " %s (", glslOpcode[eType]); + TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + bcatcstr(glsl, ")"); + if (s1ElemCount > minElemCount) + { + AddSwizzleUsingElementCount(psContext, minElemCount); + } + bcatcstr(glsl, ";\n"); + AddIndentation(psContext); + } + + if (psContext->psShader->ui32MajorVersion < 4) + { + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + } + else + { + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + } + + if (qualcommWorkaround) + { + // Using the temporary variable where we stored the result of the comparison for the ternary operator. + bformata(glsl, "!!%s ", tempVariableName); + } + else + { + bcatcstr(glsl, "(("); + TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + bcatcstr(glsl, ")"); + if (s0ElemCount > minElemCount) + { + AddSwizzleUsingElementCount(psContext, minElemCount); + } + bformata(glsl, " %s (", glslOpcode[eType]); + TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + bcatcstr(glsl, ")"); + if (s1ElemCount > minElemCount) + { + AddSwizzleUsingElementCount(psContext, minElemCount); + } + bcatcstr(glsl, ") "); + } + + if (psContext->psShader->ui32MajorVersion < 4) + { + bcatcstr(glsl, "? 1.0f : 0.0f"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + } + else + { + bcatcstr(glsl, "? 0xFFFFFFFFu : uint(0)"); // Adreno can't handle 0u (it's treated as int) + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + } + bcatcstr(glsl, ";\n"); + if (qualcommWorkaround) + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + } + } +} + +static void AddMOVBinaryOp(HLSLCrossCompilerContext* psContext, const Operand* pDst, const Operand* pSrc, uint32_t bSrcCopy, uint32_t bSaturate) +{ + bstring glsl = *psContext->currentGLSLString; + + const SHADER_VARIABLE_TYPE eSrcType = GetOperandDataType(psContext, pSrc); + uint32_t srcCount = GetNumSwizzleElements(pSrc); + uint32_t dstCount = GetNumSwizzleElements(pDst); + uint32_t bMismatched = 0; + + uint32_t ui32SrcFlags = TO_FLAG_FLOAT; + if (!bSaturate) + { + switch (eSrcType) + { + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + ui32SrcFlags = TO_FLAG_INTEGER; + break; + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + ui32SrcFlags = TO_FLAG_UNSIGNED_INTEGER; + break; + } + } + if (bSrcCopy) + { + ui32SrcFlags |= TO_FLAG_COPY; + } + + AddIndentation(psContext); + BeginAssignment(psContext, pDst, ui32SrcFlags, bSaturate); + + //Mismatched element count or destination has any swizzle + if (srcCount != dstCount || (GetFirstOperandSwizzle(psContext, pDst) != -1)) + { + bMismatched = 1; + + // Special case for immediate operands that can be folded into *vec4 + if (srcCount == 1) + { + switch (ui32SrcFlags) + { + case TO_FLAG_INTEGER: + bcatcstr(glsl, "ivec4"); + break; + case TO_FLAG_UNSIGNED_INTEGER: + bcatcstr(glsl, "uvec4"); + break; + default: + bcatcstr(glsl, "vec4"); + } + } + + bcatcstr(glsl, "("); + } + + TranslateOperand(psContext, pSrc, ui32SrcFlags); + + if (bMismatched) + { + bcatcstr(glsl, ")"); + + if (GetFirstOperandSwizzle(psContext, pDst) != -1) + { + TranslateOperandSwizzle(psContext, pDst); + } + else + { + AddSwizzleUsingElementCount(psContext, dstCount); + } + } + + EndAssignment(psContext, pDst, ui32SrcFlags, bSaturate); + bcatcstr(glsl, ";\n"); +} + +static void AddMOVCBinaryOp(HLSLCrossCompilerContext* psContext, const Operand* pDest, uint32_t bDestCopy, const Operand* src0, const Operand* src1, const Operand* src2) +{ + bstring glsl = *psContext->currentGLSLString; + + uint32_t destElemCount = GetNumSwizzleElements(pDest); + uint32_t s0ElemCount = GetNumSwizzleElements(src0); + uint32_t s1ElemCount = GetNumSwizzleElements(src1); + uint32_t s2ElemCount = GetNumSwizzleElements(src2); + uint32_t destElem; + int qualcommWorkaround = psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND; + + const char* swizzles = "xyzw"; + uint32_t eDstDataType; + const char* szVecType; + + uint32_t uDestFlags = TO_FLAG_DESTINATION; + if (bDestCopy) + { + uDestFlags |= TO_FLAG_COPY; + } + + AddIndentation(psContext); + // Qualcomm OpenGLES 3.0 bug that makes something likes this: + // temp4.xyz = vec3(floatsToInt(temp1).x != 0 ? temp2.x : temp2.x, floatsToInt(temp1).y != 0 ? temp2.y : temp2.y, floatsToInt(temp1).z != 0 ? temp2.z : temp2.z) + // to fail in the ternary operator. The workaround is to save the floatToInt(temp1) into a temp variable: + // { ivec4 cond = floatsToInt(temp1); temp4.xyz = vec3(cond.x != 0 ? temp2.x : temp2.x, cond.y != 0 ? temp2.y : temp2.y, cond.z != 0 ? temp2.z : temp2.z); } + if (qualcommWorkaround) + { + bformata(glsl, "{\n"); + ++psContext->indent; + AddIndentation(psContext); + if (s0ElemCount > 1) + bformata(glsl, "ivec%d cond = ", s0ElemCount); + else + bformata(glsl, "int cond = "); + TranslateOperand(psContext, src0, TO_FLAG_INTEGER); + bformata(glsl, ";\n"); + AddIndentation(psContext); + } + + TranslateOperand(psContext, pDest, uDestFlags); + + switch (GetOperandDataType(psContext, pDest)) + { + case SVT_UINT: + case SVT_UINT8: + case SVT_UINT16: + szVecType = "uvec"; + eDstDataType = TO_FLAG_UNSIGNED_INTEGER; + break; + case SVT_INT: + case SVT_INT12: + case SVT_INT16: + szVecType = "ivec"; + eDstDataType = TO_FLAG_INTEGER; + break; + default: + szVecType = "vec"; + eDstDataType = TO_FLAG_FLOAT; + break; + } + + if (destElemCount > 1) + { + bformata(glsl, " = %s%d(", szVecType, destElemCount); + } + else + { + bcatcstr(glsl, " = "); + } + + for (destElem = 0; destElem < destElemCount; ++destElem) + { + if (destElem > 0) + { + bcatcstr(glsl, ", "); + } + + if (qualcommWorkaround) + { + bcatcstr(glsl, "cond"); + } + else + { + TranslateOperand(psContext, src0, TO_FLAG_INTEGER); + } + + if (s0ElemCount > 1) + { + TranslateOperandSwizzle(psContext, pDest); + bformata(glsl, ".%c", swizzles[destElem]); + } + + bcatcstr(glsl, " != 0 ? "); + + TranslateOperand(psContext, src1, eDstDataType); + if (s1ElemCount > 1) + { + TranslateOperandSwizzle(psContext, pDest); + bformata(glsl, ".%c", swizzles[destElem]); + } + + bcatcstr(glsl, " : "); + + TranslateOperand(psContext, src2, eDstDataType); + if (s2ElemCount > 1) + { + TranslateOperandSwizzle(psContext, pDest); + bformata(glsl, ".%c", swizzles[destElem]); + } + } + if (destElemCount > 1) + { + bcatcstr(glsl, ");\n"); + } + else + { + bcatcstr(glsl, ";\n"); + } + + if (qualcommWorkaround) + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + } +} + +void CallBinaryOp(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, uint32_t dataType) +{ + bstring glsl = *psContext->currentGLSLString; + uint32_t src1SwizCount = GetNumSwizzleElements(&psInst->asOperands[src1]); + uint32_t src0SwizCount = GetNumSwizzleElements(&psInst->asOperands[src0]); + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + + AddIndentation(psContext); + // Qualcomm OpenGLES 3.0 drivers don't support bitwise operators for vectors. + // Because of this we need to do the operation per component. + bool qualcommWorkaround = (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) != 0; + bool isBitwiseOperator = psInst->eOpcode == OPCODE_AND || psInst->eOpcode == OPCODE_OR || psInst->eOpcode == OPCODE_XOR; + const char* swizzleString[] = { ".x", ".y", ".z", ".w" }; + if (src1SwizCount == src0SwizCount == dstSwizCount) + { + BeginAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + if (qualcommWorkaround && isBitwiseOperator && src0SwizCount > 1) + { + for (uint32_t i = 0; i < src0SwizCount; ++i) + { + if (i > 0) + { + bcatcstr(glsl, ", "); + } + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_NONE | dataType); + bformata(glsl, "%s", swizzleString[i]); + bformata(glsl, " %s ", name); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_NONE | dataType); + bformata(glsl, "%s", swizzleString[i]); + } + } + else + { + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_NONE | dataType); + bformata(glsl, " %s ", name); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_NONE | dataType); + } + EndAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + else + { + //Upconvert the inputs to vec4 then apply the dest swizzle. + BeginAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + if (dataType == TO_FLAG_UNSIGNED_INTEGER) + { + bcatcstr(glsl, "uvec4("); + } + else if (dataType == TO_FLAG_INTEGER) + { + bcatcstr(glsl, "ivec4("); + } + else + { + bcatcstr(glsl, "vec4("); + } + + if (qualcommWorkaround && isBitwiseOperator && src0SwizCount > 1) + { + for (uint32_t i = 0; i < src0SwizCount; ++i) + { + if (i > 0) + { + bcatcstr(glsl, ", "); + } + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_NONE | dataType); + bformata(glsl, "%s", swizzleString[i]); + bformata(glsl, " %s ", name); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_NONE | dataType); + bformata(glsl, "%s", swizzleString[i]); + } + } + else + { + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_NONE | dataType); + bformata(glsl, " %s ", name); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_NONE | dataType); + } + bcatcstr(glsl, ")"); + //Limit src swizzles based on dest swizzle + //e.g. given hlsl asm: add r0.xy, v0.xyxx, l(0.100000, 0.000000, 0.000000, 0.000000) + //the two sources must become vec2 + //Temp0.xy = Input0.xyxx + vec4(0.100000, 0.000000, 0.000000, 0.000000); + //becomes + //Temp0.xy = vec4(Input0.xyxx + vec4(0.100000, 0.000000, 0.000000, 0.000000)).xy; + + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + EndAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } +} + +void CallTernaryOp(HLSLCrossCompilerContext* psContext, const char* op1, const char* op2, Instruction* psInst, + int dest, int src0, int src1, int src2, uint32_t dataType) +{ + bstring glsl = *psContext->currentGLSLString; + uint32_t src2SwizCount = GetNumSwizzleElements(&psInst->asOperands[src2]); + uint32_t src1SwizCount = GetNumSwizzleElements(&psInst->asOperands[src1]); + uint32_t src0SwizCount = GetNumSwizzleElements(&psInst->asOperands[src0]); + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + + AddIndentation(psContext); + + if (src1SwizCount == src0SwizCount == src2SwizCount == dstSwizCount) + { + BeginAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_NONE | dataType); + bformata(glsl, " %s ", op1); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_NONE | dataType); + bformata(glsl, " %s ", op2); + TranslateOperand(psContext, &psInst->asOperands[src2], TO_FLAG_NONE | dataType); + EndAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + else + { + BeginAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + if (dataType == TO_FLAG_UNSIGNED_INTEGER) + { + bcatcstr(glsl, "uvec4("); + } + else if (dataType == TO_FLAG_INTEGER) + { + bcatcstr(glsl, "ivec4("); + } + else + { + bcatcstr(glsl, "vec4("); + } + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_NONE | dataType); + bformata(glsl, " %s ", op1); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_NONE | dataType); + bformata(glsl, " %s ", op2); + TranslateOperand(psContext, &psInst->asOperands[src2], TO_FLAG_NONE | dataType); + bcatcstr(glsl, ")"); + //Limit src swizzles based on dest swizzle + //e.g. given hlsl asm: add r0.xy, v0.xyxx, l(0.100000, 0.000000, 0.000000, 0.000000) + //the two sources must become vec2 + //Temp0.xy = Input0.xyxx + vec4(0.100000, 0.000000, 0.000000, 0.000000); + //becomes + //Temp0.xy = vec4(Input0.xyxx + vec4(0.100000, 0.000000, 0.000000, 0.000000)).xy; + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + EndAssignment(psContext, &psInst->asOperands[dest], dataType, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } +} + +void CallHelper3(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, int src2) +{ + bstring glsl = *psContext->currentGLSLString; + AddIndentation(psContext); + + BeginAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_FLOAT, psInst->bSaturate); + + bcatcstr(glsl, "vec4("); + + bcatcstr(glsl, name); + bcatcstr(glsl, "("); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_FLOAT); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[src2], TO_FLAG_FLOAT); + bcatcstr(glsl, "))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + EndAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); +} + +void CallHelper2(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1) +{ + bstring glsl = *psContext->currentGLSLString; + AddIndentation(psContext); + + BeginAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_FLOAT, psInst->bSaturate); + + bcatcstr(glsl, "vec4("); + + bcatcstr(glsl, name); + bcatcstr(glsl, "("); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_FLOAT); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_FLOAT); + bcatcstr(glsl, "))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + EndAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); +} + +void CallHelper2Int(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1) +{ + bstring glsl = *psContext->currentGLSLString; + AddIndentation(psContext); + + BeginAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_INTEGER, psInst->bSaturate); + + bcatcstr(glsl, "ivec4("); + + bcatcstr(glsl, name); + bcatcstr(glsl, "(int("); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_INTEGER); + bcatcstr(glsl, "), int("); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_INTEGER); + bcatcstr(glsl, ")))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + EndAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); +} +void CallHelper2UInt(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1) +{ + bstring glsl = *psContext->currentGLSLString; + AddIndentation(psContext); + + BeginAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + + bcatcstr(glsl, "uvec4("); + + bcatcstr(glsl, name); + bcatcstr(glsl, "(uint("); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, "), uint("); + TranslateOperand(psContext, &psInst->asOperands[src1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, ")))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + EndAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); +} + +void CallHelper1(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0) +{ + bstring glsl = *psContext->currentGLSLString; + + AddIndentation(psContext); + + BeginAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_FLOAT, psInst->bSaturate); + + // Qualcomm driver workaround + // Example: Instead of Temp1.xyz = (vec4(log2(Temp0[0].xyzx)).xyz); we write + // Temp1.xyz = (log2(vec4(Temp0[0].xyzx).xyz)); + if (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) + { + bcatcstr(glsl, name); + bcatcstr(glsl, "("); + bcatcstr(glsl, "vec4("); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_FLOAT); + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + bcatcstr(glsl, ")"); + } + else + { + bcatcstr(glsl, "vec4("); + bcatcstr(glsl, name); + bcatcstr(glsl, "("); + TranslateOperand(psContext, &psInst->asOperands[src0], TO_FLAG_FLOAT); + bcatcstr(glsl, "))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[dest]); + } + EndAssignment(psContext, &psInst->asOperands[dest], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); +} + +//Makes sure the texture coordinate swizzle is appropriate for the texture type. +//i.e. vecX for X-dimension texture. +//Currently supports floating point coord only, so not used for texelFetch. +static void TranslateTexCoord(HLSLCrossCompilerContext* psContext, + const RESOURCE_DIMENSION eResDim, + Operand* psTexCoordOperand) +{ + unsigned int uNumCoords = psTexCoordOperand->iNumComponents; + int constructor = 0; + bstring glsl = *psContext->currentGLSLString; + + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + //Vec1 texcoord. Mask out the other components. + psTexCoordOperand->aui32Swizzle[1] = 0xFFFFFFFF; + psTexCoordOperand->aui32Swizzle[2] = 0xFFFFFFFF; + psTexCoordOperand->aui32Swizzle[3] = 0xFFFFFFFF; + if (psTexCoordOperand->eType == OPERAND_TYPE_IMMEDIATE32 || + psTexCoordOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + psTexCoordOperand->iNumComponents = 1; + } + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + //Vec2 texcoord. Mask out the other components. + psTexCoordOperand->aui32Swizzle[2] = 0xFFFFFFFF; + psTexCoordOperand->aui32Swizzle[3] = 0xFFFFFFFF; + if (psTexCoordOperand->eType == OPERAND_TYPE_IMMEDIATE32 || + psTexCoordOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + psTexCoordOperand->iNumComponents = 2; + } + if (psTexCoordOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + constructor = 1; + bcatcstr(glsl, "vec2("); + } + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + //Vec3 texcoord. Mask out the other component. + psTexCoordOperand->aui32Swizzle[3] = 0xFFFFFFFF; + if (psTexCoordOperand->eType == OPERAND_TYPE_IMMEDIATE32 || + psTexCoordOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + psTexCoordOperand->iNumComponents = 3; + } + if (psTexCoordOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + constructor = 1; + bcatcstr(glsl, "vec3("); + } + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + uNumCoords = 4; + if (psTexCoordOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + constructor = 1; + bcatcstr(glsl, "vec4("); + } + break; + } + default: + { + ASSERT(0); + break; + } + } + + //Mask out the other components. + switch (psTexCoordOperand->eSelMode) + { + case OPERAND_4_COMPONENT_SELECT_1_MODE: + ASSERT(uNumCoords == 1); + break; + case OPERAND_4_COMPONENT_SWIZZLE_MODE: + while (uNumCoords < 4) + { + psTexCoordOperand->aui32Swizzle[uNumCoords] = 0xFFFFFFFF; + ++uNumCoords; + } + break; + case OPERAND_4_COMPONENT_MASK_MODE: + if (psTexCoordOperand->ui32CompMask < 4) + { + psTexCoordOperand->ui32CompMask = + (uNumCoords > 0) * OPERAND_4_COMPONENT_MASK_X | + (uNumCoords > 1) * OPERAND_4_COMPONENT_MASK_Y | + (uNumCoords > 2) * OPERAND_4_COMPONENT_MASK_Z; + } + break; + } + TranslateOperand(psContext, psTexCoordOperand, TO_FLAG_FLOAT); + + if (constructor) + { + bcatcstr(glsl, ")"); + } +} + +static int GetNumTextureDimensions(HLSLCrossCompilerContext* psContext, + const RESOURCE_DIMENSION eResDim) +{ + (void)(psContext); + + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + return 1; + } + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + case RESOURCE_DIMENSION_TEXTURECUBE: + { + return 2; + } + + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + return 3; + } + default: + { + ASSERT(0); + break; + } + } + return 0; +} + +void GetResInfoData(HLSLCrossCompilerContext* psContext, Instruction* psInst, int index) +{ + bstring glsl = *psContext->currentGLSLString; + const RESINFO_RETURN_TYPE eResInfoReturnType = psInst->eResInfoReturnType; + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + + //[width, height, depth or array size, total-mip-count] + if (index < 3) + { + int dim = GetNumTextureDimensions(psContext, eResDim); + + if (dim < (index + 1)) + { + bcatcstr(glsl, "0"); + } + else + { + if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) + { + bformata(glsl, "ivec%d(textureSize(", dim); + } + else if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_RCPFLOAT) + { + bformata(glsl, "vec%d(1.0f) / vec%d(textureSize(", dim, dim); + } + else + { + bformata(glsl, "vec%d(textureSize(", dim); + } + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, "))"); + + switch (index) + { + case 0: + bcatcstr(glsl, ".x"); + break; + case 1: + bcatcstr(glsl, ".y"); + break; + case 2: + bcatcstr(glsl, ".z"); + break; + } + } + } + else + { + bcatcstr(glsl, "textureQueryLevels("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ")"); + } +} + +uint32_t GetReturnTypeToFlags(RESOURCE_RETURN_TYPE eReturnType) +{ + switch (eReturnType) + { + case RETURN_TYPE_FLOAT: + return TO_FLAG_FLOAT; + case RETURN_TYPE_UINT: + return TO_FLAG_UNSIGNED_INTEGER; + case RETURN_TYPE_SINT: + return TO_FLAG_INTEGER; + case RETURN_TYPE_DOUBLE: + return TO_FLAG_DOUBLE; + } + ASSERT(0); + return TO_FLAG_NONE; +} + +uint32_t GetResourceReturnTypeToFlags(ResourceGroup eGroup, uint32_t ui32BindPoint, HLSLCrossCompilerContext* psContext) +{ + ResourceBinding* psBinding; + if (GetResourceFromBindingPoint(eGroup, ui32BindPoint, &psContext->psShader->sInfo, &psBinding)) + { + return GetReturnTypeToFlags(psBinding->ui32ReturnType); + } + ASSERT(0); + return TO_FLAG_NONE; +} + +#define TEXSMP_FLAG_NONE 0x0 +#define TEXSMP_FLAG_LOD 0x1 //LOD comes from operand +#define TEXSMP_FLAG_COMPARE 0x2 +#define TEXSMP_FLAG_FIRSTLOD 0x4 //LOD is 0 +#define TEXSMP_FLAG_BIAS 0x8 +#define TEXSMP_FLAGS_GRAD 0x10 +static void TranslateTextureSample(HLSLCrossCompilerContext* psContext, Instruction* psInst, uint32_t ui32Flags) +{ + bstring glsl = *psContext->currentGLSLString; + + const char* funcName = "texture"; + const char* offset = ""; + const char* depthCmpCoordType = ""; + const char* gradSwizzle = ""; + uint32_t sampleTypeToFlags = TO_FLAG_FLOAT; + + uint32_t ui32NumOffsets = 0; + + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + + const int iHaveOverloadedTexFuncs = HaveOverloadedTextureFuncs(psContext->psShader->eTargetLanguage); + + ASSERT(psInst->asOperands[2].ui32RegisterNumber < MAX_TEXTURES); + + if (psInst->bAddressOffset) + { + offset = "Offset"; + } + + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + depthCmpCoordType = "vec2"; + gradSwizzle = ".x"; + ui32NumOffsets = 1; + if (!iHaveOverloadedTexFuncs) + { + funcName = "texture1D"; + if (ui32Flags & TEXSMP_FLAG_COMPARE) + { + funcName = "shadow1D"; + } + } + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + depthCmpCoordType = "vec3"; + gradSwizzle = ".xy"; + ui32NumOffsets = 2; + if (!iHaveOverloadedTexFuncs) + { + funcName = "texture2D"; + if (ui32Flags & TEXSMP_FLAG_COMPARE) + { + funcName = "shadow2D"; + } + } + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + depthCmpCoordType = "vec3"; + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + if (!iHaveOverloadedTexFuncs) + { + funcName = "textureCube"; + } + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + depthCmpCoordType = "vec4"; + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + if (!iHaveOverloadedTexFuncs) + { + funcName = "texture3D"; + } + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + depthCmpCoordType = "vec3"; + gradSwizzle = ".x"; + ui32NumOffsets = 1; + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + depthCmpCoordType = "vec4"; + gradSwizzle = ".xy"; + ui32NumOffsets = 2; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + if (ui32Flags & TEXSMP_FLAG_COMPARE) + { + //Special. Reference is a separate argument. + AddIndentation(psContext); + sampleTypeToFlags = TO_FLAG_FLOAT; + BeginAssignment(psContext, &psInst->asOperands[0], sampleTypeToFlags, psInst->bSaturate); + if (ui32Flags & (TEXSMP_FLAG_LOD | TEXSMP_FLAG_FIRSTLOD)) + { + bcatcstr(glsl, "(vec4(textureLod("); + } + else + { + bcatcstr(glsl, "(vec4(texture("); + } + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1); + bcatcstr(glsl, ","); + TranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(glsl, ","); + //.z = reference. + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_FLOAT); + + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(glsl, ", 0.0"); + } + + bcatcstr(glsl, "))"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + bcatcstr(glsl, ")"); + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], sampleTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + return; + } + + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (ui32Flags & TEXSMP_FLAG_COMPARE) + { + //For non-cubeMap Arrays the reference value comes from the + //texture coord vector in GLSL. For cubmap arrays there is a + //separate parameter. + //It is always separate paramter in HLSL. + AddIndentation(psContext); + sampleTypeToFlags = TO_FLAG_FLOAT; + BeginAssignment(psContext, &psInst->asOperands[0], sampleTypeToFlags, psInst->bSaturate); + + if (ui32Flags & (TEXSMP_FLAG_LOD | TEXSMP_FLAG_FIRSTLOD)) + { + bformata(glsl, "(vec4(%sLod%s(", funcName, offset); + } + else + { + bformata(glsl, "(vec4(%s%s(", funcName, offset); + } + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1); + bformata(glsl, ", %s(", depthCmpCoordType); + TranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(glsl, ","); + //.z = reference. + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_FLOAT); + bcatcstr(glsl, ")"); + + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(glsl, ", 0.0"); + } + + bcatcstr(glsl, "))"); + } + else + { + AddIndentation(psContext); + sampleTypeToFlags = GetResourceReturnTypeToFlags(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, psContext); + BeginAssignment(psContext, &psInst->asOperands[0], sampleTypeToFlags, psInst->bSaturate); + if (ui32Flags & (TEXSMP_FLAG_LOD | TEXSMP_FLAG_FIRSTLOD)) + { + bformata(glsl, "(%sLod%s(", funcName, offset); + } + else + if (ui32Flags & TEXSMP_FLAGS_GRAD) + { + bformata(glsl, "(%sGrad%s(", funcName, offset); + } + else + { + bformata(glsl, "(%s%s(", funcName, offset); + } + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 0); + bcatcstr(glsl, ", "); + TranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + + if (ui32Flags & (TEXSMP_FLAG_LOD)) + { + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_FLOAT); + if (psContext->psShader->ui32MajorVersion < 4) + { + bcatcstr(glsl, ".w"); + } + } + else + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(glsl, ", 0.0"); + } + else + if (ui32Flags & TEXSMP_FLAGS_GRAD) + { + bcatcstr(glsl, ", vec4("); + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_FLOAT);//dx + bcatcstr(glsl, ")"); + bcatcstr(glsl, gradSwizzle); + bcatcstr(glsl, ", vec4("); + TranslateOperand(psContext, &psInst->asOperands[5], TO_FLAG_FLOAT);//dy + bcatcstr(glsl, ")"); + bcatcstr(glsl, gradSwizzle); + } + + if (psInst->bAddressOffset) + { + if (ui32NumOffsets == 1) + { + bformata(glsl, ", %d", + psInst->iUAddrOffset); + } + else + if (ui32NumOffsets == 2) + { + bformata(glsl, ", ivec2(%d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset); + } + else + if (ui32NumOffsets == 3) + { + bformata(glsl, ", ivec3(%d, %d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset, + psInst->iWAddrOffset); + } + } + + if (ui32Flags & (TEXSMP_FLAG_BIAS)) + { + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_FLOAT); + } + + bcatcstr(glsl, ")"); + } + + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + bcatcstr(glsl, ")"); + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], sampleTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); +} + +static ShaderVarType* LookupStructuredVarExtended(HLSLCrossCompilerContext* psContext, + Operand* psResource, + Operand* psByteOffset, + uint32_t ui32Component, + uint32_t* swizzle) +{ + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + uint32_t aui32Swizzle[4] = {OPERAND_4_COMPONENT_X}; + int byteOffset = psByteOffset ? ((int*)psByteOffset->afImmediates)[0] + 4 * ui32Component : 0; + int vec4Offset = byteOffset >> 4; + int32_t index = -1; + int32_t rebase = -1; + int found; + //TODO: multi-component stores and vector writes need testing. + + //aui32Swizzle[0] = psInst->asOperands[0].aui32Swizzle[component]; + + switch (byteOffset % 16) + { + case 0: + aui32Swizzle[0] = 0; + break; + case 4: + aui32Swizzle[0] = 1; + break; + case 8: + aui32Swizzle[0] = 2; + break; + case 12: + aui32Swizzle[0] = 3; + break; + } + + switch (psResource->eType) + { + case OPERAND_TYPE_RESOURCE: + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + GetConstantBufferFromBindingPoint(RGROUP_UAV, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + //dcl_tgsm_structured defines the amount of memory and a stride. + ASSERT(psResource->ui32RegisterNumber < MAX_GROUPSHARED); + ASSERT(swizzle == NULL); + return &psContext->psShader->sGroupSharedVarType[psResource->ui32RegisterNumber]; + } + default: + ASSERT(0); + break; + } + + found = GetShaderVarFromOffset(vec4Offset, aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + ASSERT(found); + + if (swizzle) + { + // Assuming the components are 4 bytes in length + const int bytesPerComponent = 4; + // Calculate the variable swizzling based on the byteOffset and the position of the variable in the structure + ASSERT((byteOffset - psVarType->Offset) % 4 == 0); + *swizzle = (byteOffset - psVarType->Offset) / bytesPerComponent; + ASSERT(*swizzle < 4); + } + + return psVarType; +} + +static ShaderVarType* LookupStructuredVar(HLSLCrossCompilerContext* psContext, + Operand* psResource, + Operand* psByteOffset, + uint32_t ui32Component) +{ + return LookupStructuredVarExtended(psContext, psResource, psByteOffset, ui32Component, NULL); +} + +static void TranslateShaderStorageVarName(bstring output, Shader* psShader, const Operand* operand, int structured) +{ + bstring varName = bfromcstr(""); + if (operand->eType == OPERAND_TYPE_RESOURCE) + { + if (structured) + { + bformata(varName, "StructuredRes%d", operand->ui32RegisterNumber); + } + else + { + bformata(varName, "RawRes%d", operand->ui32RegisterNumber); + } + } + else if(operand->eType == OPERAND_TYPE_UNORDERED_ACCESS_VIEW) + { + bformata(varName, "UAV%d", operand->ui32RegisterNumber); + } + else + { + ASSERT(0); + } + ShaderVarName(output, psShader, bstr2cstr(varName, '\0')); + bdestroy(varName); +} + +static void TranslateShaderStorageStore(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentGLSLString; + ShaderVarType* psVarType = NULL; + int component; + int srcComponent = 0; + + Operand* psDest = 0; + Operand* psDestAddr = 0; + Operand* psDestByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_STORE_STRUCTURED: + psDest = &psInst->asOperands[0]; + psDestAddr = &psInst->asOperands[1]; + psDestByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + break; + case OPCODE_STORE_RAW: + psDest = &psInst->asOperands[0]; + psDestByteOff = &psInst->asOperands[1]; + psSrc = &psInst->asOperands[2]; + break; + } + + for (component = 0; component < 4; component++) + { + const char* swizzleString[] = { ".x", ".y", ".z", ".w" }; + ASSERT(psInst->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psInst->asOperands[0].ui32CompMask & (1 << component)) + { + uint32_t swizzle = 0; + if (structured && psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + psVarType = LookupStructuredVarExtended(psContext, psDest, psDestByteOff, component, &swizzle); + } + + AddIndentation(psContext); + TranslateShaderStorageVarName(glsl, psContext->psShader, psDest, structured); + bformata(glsl, "["); + if (structured) //Dest address and dest byte offset + { + if (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + TranslateOperand(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, "].value["); + TranslateOperand(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, " >> 2u ");//bytes to floats + } + else + { + TranslateOperand(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + } + else + { + TranslateOperand(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + + //RAW: change component using index offset + if (!structured || (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY)) + { + bformata(glsl, " + %d", component); + } + + bformata(glsl, "]"); + + if (structured && psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + if (strcmp(psVarType->Name, "$Element") != 0) + { + bcatcstr(glsl, "."); + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + } + + if (psVarType->Columns > 1) + { + bformata(glsl, swizzleString[swizzle]); + } + } + + + if (structured) + { + uint32_t flags = TO_FLAG_UNSIGNED_INTEGER; + if (psVarType) + { + if (psVarType->Type == SVT_INT) + { + flags = TO_FLAG_INTEGER; + } + else if (psVarType->Type == SVT_FLOAT) + { + flags = TO_FLAG_NONE; + } + } + //TGSM always uint + bformata(glsl, " = ("); + TranslateOperand(psContext, psSrc, flags); + } + else + { + //Dest type is currently always a uint array. + bformata(glsl, " = ("); + TranslateOperand(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER); + } + + if (GetNumSwizzleElements(psSrc) > 1) + { + bformata(glsl, swizzleString[srcComponent++]); + } + + //Double takes an extra slot. + if (psVarType && psVarType->Type == SVT_DOUBLE) + { + if (structured && psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + bcatcstr(glsl, ")"); + } + component++; + } + + bformata(glsl, ");\n"); + } + } +} + +static void TranslateShaderPLSStore(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentGLSLString; + ShaderVarType* psVarType = NULL; + int component; + int srcComponent = 0; + + Operand* psDest = 0; + Operand* psDestAddr = 0; + Operand* psDestByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_STORE_STRUCTURED: + psDest = &psInst->asOperands[0]; + psDestAddr = &psInst->asOperands[1]; + psDestByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + break; + case OPCODE_STORE_RAW: + default: + ASSERT(0); + } + + ASSERT(structured); + + for (component = 0; component < 4; component++) + { + const char* swizzleString[] = { ".x", ".y", ".z", ".w" }; + ASSERT(psInst->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psInst->asOperands[0].ui32CompMask & (1 << component)) + { + + ASSERT(psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY); + + psVarType = LookupStructuredVar(psContext, psDest, psDestByteOff, component); + + AddIndentation(psContext); + + if (structured && psDest->eType == OPERAND_TYPE_RESOURCE) + { + bstring varName = bfromcstralloc(16, ""); + bformata(varName, "StructuredRes%d", psDest->ui32RegisterNumber); + ShaderVarName(glsl, psContext->psShader, bstr2cstr(varName, '\0')); + bdestroy(varName); + } + else + { + TranslateOperand(psContext, psDest, TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY); + } + + ASSERT(strcmp(psVarType->Name, "$Element") != 0); + + bcatcstr(glsl, "."); + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + + if (psVarType->Class == SVC_VECTOR) + { + int byteOffset = ((int*)psDestByteOff->afImmediates)[0] + 4 * (psDest->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psDest->aui32Swizzle[component] : component); + int byteOffsetOfVar = psVarType->Offset; + unsigned int startComponent = (byteOffset - byteOffsetOfVar) >> 2; + unsigned int s = startComponent; + + bformata(glsl, "%s", swizzleString[s]); + } + + uint32_t flags = TO_FLAG_UNSIGNED_INTEGER; + if (psVarType) + { + if (psVarType->Type == SVT_INT) + { + flags = TO_FLAG_INTEGER; + } + else if (psVarType->Type == SVT_FLOAT) + { + flags = TO_FLAG_NONE; + } + else + { + ASSERT(0); + } + } + //TGSM always uint + bformata(glsl, " = ("); + TranslateOperand(psContext, psSrc, flags); + + + + if (GetNumSwizzleElements(psSrc) > 1) + { + bformata(glsl, swizzleString[srcComponent++]); + } + + //Double takes an extra slot. + if (psVarType && psVarType->Type == SVT_DOUBLE) + { + if (structured && psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + bcatcstr(glsl, ")"); + } + component++; + } + + bformata(glsl, ");\n"); + } + } +} + +static void TranslateShaderStorageLoad(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentGLSLString; + ShaderVarType* psVarType = NULL; + uint32_t aui32Swizzle[4] = {OPERAND_4_COMPONENT_X}; + uint32_t ui32DataTypeFlag = TO_FLAG_INTEGER; + int component; + int destComponent = 0; + + Operand* psDest = 0; + Operand* psSrcAddr = 0; + Operand* psSrcByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_LD_STRUCTURED: + psDest = &psInst->asOperands[0]; + psSrcAddr = &psInst->asOperands[1]; + psSrcByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + break; + case OPCODE_LD_RAW: + psDest = &psInst->asOperands[0]; + psSrcByteOff = &psInst->asOperands[1]; + psSrc = &psInst->asOperands[2]; + break; + } + + if (psInst->eOpcode == OPCODE_LD_RAW) + { + unsigned int ui32CompNum = GetNumSwizzleElements(psDest); + + for (component = 0; component < 4; component++) + { + const char* swizzleString [] = { "x", "y", "z", "w" }; + ASSERT(psDest->eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psDest->ui32CompMask & (1 << component)) + { + int addedBitcast = 0; + + if (structured) + { + psVarType = LookupStructuredVar(psContext, psSrc, psSrcByteOff, psSrc->aui32Swizzle[component]); + } + + AddIndentation(psContext); + + aui32Swizzle[0] = psSrc->aui32Swizzle[component]; + + if (ui32CompNum > 1) + { + BeginAssignmentEx(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate, swizzleString[destComponent++]); + } + else + { + BeginAssignment(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate); + } + + if (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + // unknown how to make this without TO_FLAG_NAME_ONLY + bcatcstr(glsl, "uintBitsToFloat("); + addedBitcast = 1; + + TranslateOperand(psContext, psSrc, ui32DataTypeFlag & TO_FLAG_NAME_ONLY); + + if (((int*)psSrcByteOff->afImmediates)[0] == 0) + { + bformata(glsl, "[0"); + } + else + { + bformata(glsl, "[(("); + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_INTEGER); + bcatcstr(glsl, ") >> 2u)"); + } + } + else + { + bstring varName = bfromcstralloc(16, ""); + bformata(varName, "RawRes%d", psSrc->ui32RegisterNumber); + + ShaderVarName(glsl, psContext->psShader, bstr2cstr(varName, '\0')); + bcatcstr(glsl, "[(("); + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_INTEGER); + bcatcstr(glsl, ") >> 2u)"); + + bdestroy(varName); + } + + if (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE && psSrc->aui32Swizzle[component] != 0) + { + bformata(glsl, " + %d", psSrc->aui32Swizzle[component]); + } + bcatcstr(glsl, "]"); + + if (addedBitcast) + { + bcatcstr(glsl, ")"); + } + + EndAssignment(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate); + bformata(glsl, ";\n"); + } + } + } + else + { + unsigned int ui32CompNum = GetNumSwizzleElements(psDest); + + //(int)GetNumSwizzleElements(&psInst->asOperands[0]) + for (component = 0; component < 4; component++) + { + const char* swizzleString [] = { "x", "y", "z", "w" }; + ASSERT(psDest->eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psDest->ui32CompMask & (1 << component)) + { + int addedBitcast = 0; + + psVarType = LookupStructuredVar(psContext, psSrc, psSrcByteOff, psSrc->aui32Swizzle[component]); + + AddIndentation(psContext); + + aui32Swizzle[0] = psSrc->aui32Swizzle[component]; + + if (ui32CompNum > 1) + { + BeginAssignmentEx(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate, swizzleString[destComponent++]); + } + else + { + BeginAssignment(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate); + } + + if (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + // unknown how to make this without TO_FLAG_NAME_ONLY + if (psVarType->Type == SVT_UINT) + { + bcatcstr(glsl, "uintBitsToFloat("); + addedBitcast = 1; + } + else if (psVarType->Type == SVT_INT) + { + bcatcstr(glsl, "intBitsToFloat("); + addedBitcast = 1; + } + else if (psVarType->Type == SVT_DOUBLE) + { + bcatcstr(glsl, "unpackDouble2x32("); + addedBitcast = 1; + } + + // input already in uints + TranslateOperand(psContext, psSrc, TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "["); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "].value[("); + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, " >> 2u)]"); + } + else + { + ConstantBuffer* psCBuf = NULL; + uint32_t swizzle = 0; + psVarType = LookupStructuredVarExtended(psContext, psSrc, psSrcByteOff, psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component, &swizzle); + GetConstantBufferFromBindingPoint(RGROUP_UAV, psSrc->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + if (psVarType->Type == SVT_UINT) + { + bcatcstr(glsl, "uintBitsToFloat("); + addedBitcast = 1; + } + else if (psVarType->Type == SVT_INT) + { + bcatcstr(glsl, "intBitsToFloat("); + addedBitcast = 1; + } + else if (psVarType->Type == SVT_DOUBLE) + { + bcatcstr(glsl, "unpackDouble2x32("); + addedBitcast = 1; + } + + if (psSrc->eType == OPERAND_TYPE_UNORDERED_ACCESS_VIEW) + { + TranslateShaderStorageVarName(glsl, psContext->psShader, psSrc, 1); + bformata(glsl, "["); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + if (strcmp(psVarType->Name, "$Element") != 0) + { + bcatcstr(glsl, "."); + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + } + + if (psVarType->Columns > 1) + { + bformata(glsl, ".%s", swizzleString[swizzle]); + } + } + else if (psSrc->eType == OPERAND_TYPE_RESOURCE) + { + TranslateShaderStorageVarName(glsl, psContext->psShader, psSrc, 1); + bcatcstr(glsl, "["); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + + if (strcmp(psVarType->Name, "$Element") != 0) + { + bcatcstr(glsl, "."); + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + } + + if (psVarType->Class == SVC_SCALAR) + { + } + else if (psVarType->Class == SVC_VECTOR) + { + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + int byteOffsetOfVar = psVarType->Offset; + unsigned int startComponent = (byteOffset - byteOffsetOfVar) >> 2; + unsigned int s = startComponent; + + bcatcstr(glsl, "."); +#if 0 + for (s = startComponent; s < min(min(psVarType->Columns, 4U - component), ui32CompNum); ++s) +#endif + bformata(glsl, "%s", swizzleString[s]); + } + else if (psVarType->Class == SVC_MATRIX_ROWS) + { + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + int byteOffsetOfVar = psVarType->Offset; + unsigned int startRow = ((byteOffset - byteOffsetOfVar) >> 2) / psVarType->Columns; + unsigned int startComponent = ((byteOffset - byteOffsetOfVar) >> 2) % psVarType->Columns; + unsigned int s = startComponent; + + bformata(glsl, "[%d]", startRow); + bcatcstr(glsl, "."); +#if 0 + for (s = startComponent; s < min(min(psVarType->Rows, 4U - component), ui32CompNum); ++s) +#endif + bformata(glsl, "%s", swizzleString[s]); + } + else if (psVarType->Class == SVC_MATRIX_COLUMNS) + { + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + int byteOffsetOfVar = psVarType->Offset; + unsigned int startCol = ((byteOffset - byteOffsetOfVar) >> 2) / psVarType->Rows; + unsigned int startComponent = ((byteOffset - byteOffsetOfVar) >> 2) % psVarType->Rows; + unsigned int s = startComponent; + + bformata(glsl, "[%d]", startCol); + bcatcstr(glsl, "."); +#if 0 + for (s = startComponent; s < min(min(psVarType->Columns, 4U - component), ui32CompNum); ++s) +#endif + bformata(glsl, "%s", swizzleString[s]); + } + else + { + //assert(0); + } + } + else + { + TranslateOperand(psContext, psSrc, ui32DataTypeFlag & TO_FLAG_NAME_ONLY); + bformata(glsl, "["); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "]."); + + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + } + + if (psVarType->Type == SVT_DOUBLE) + { + component++; // doubles take up 2 slots + } +#if 0 + if (psVarType->Class == SVC_VECTOR) + { + component += min(psVarType->Columns, ui32CompNum) - 1; // vector take up various slots + } + if (psVarType->Class == SVC_MATRIX_ROWS) + { + component += min(psVarType->Columns * psVarType->Rows, ui32CompNum) - 1; // matrix take up various slots + } + if (psVarType->Class == SVC_MATRIX_COLUMNS) + { + component += min(psVarType->Columns * psVarType->Rows, ui32CompNum) - 1; // matrix take up various slots + } +#endif + } + + if (addedBitcast) + { + bcatcstr(glsl, ")"); + } + + EndAssignment(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate); + bformata(glsl, ";\n"); + } + } + } +} + +static void TranslateShaderPLSLoad(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentGLSLString; + ShaderVarType* psVarType = NULL; + uint32_t aui32Swizzle[4] = { OPERAND_4_COMPONENT_X }; + int component; + int destComponent = 0; + + Operand* psDest = 0; + Operand* psSrcAddr = 0; + Operand* psSrcByteOff = 0; + Operand* psSrc = 0; + + switch (psInst->eOpcode) + { + case OPCODE_LD_STRUCTURED: + psDest = &psInst->asOperands[0]; + psSrcAddr = &psInst->asOperands[1]; + psSrcByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + break; + case OPCODE_LD_RAW: + default: + ASSERT(0); + } + + unsigned int ui32CompNum = GetNumSwizzleElements(psDest); + + for (component = 0; component < 4; component++) + { + const char* swizzleString[] = { "x", "y", "z", "w" }; + ASSERT(psDest->eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psDest->ui32CompMask & (1 << component)) + { + int addedBitcast = 0; + + psVarType = LookupStructuredVar(psContext, psSrc, psSrcByteOff, psSrc->aui32Swizzle[component]); + + AddIndentation(psContext); + + aui32Swizzle[0] = psSrc->aui32Swizzle[component]; + + if (ui32CompNum > 1) + { + BeginAssignmentEx(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate, swizzleString[destComponent++]); + } + else + { + BeginAssignment(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate); + } + + ASSERT(psSrc->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY); + + ConstantBuffer* psCBuf = NULL; + psVarType = LookupStructuredVar(psContext, psSrc, psSrcByteOff, psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + GetConstantBufferFromBindingPoint(RGROUP_UAV, psSrc->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + if (psVarType->Type == SVT_UINT) + { + bcatcstr(glsl, "uintBitsToFloat("); + addedBitcast = 1; + } + else if (psVarType->Type == SVT_INT) + { + bcatcstr(glsl, "intBitsToFloat("); + addedBitcast = 1; + } + else if (psVarType->Type == SVT_DOUBLE) + { + ASSERT(0); + } + + ASSERT(psSrc->eType == OPERAND_TYPE_UNORDERED_ACCESS_VIEW); + + TranslateOperand(psContext, psSrc, TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY); + ASSERT(strcmp(psVarType->Name, "$Element") != 0); + + bcatcstr(glsl, "."); + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + + ASSERT(psVarType->Type != SVT_DOUBLE); + ASSERT(psVarType->Class != SVC_MATRIX_ROWS); + ASSERT(psVarType->Class != SVC_MATRIX_COLUMNS); + + if (psVarType->Class == SVC_VECTOR) + { + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + int byteOffsetOfVar = psVarType->Offset; + unsigned int startComponent = (byteOffset - byteOffsetOfVar) >> 2; + unsigned int s = startComponent; + + bcatcstr(glsl, "."); + bformata(glsl, "%s", swizzleString[s]); + } + + if (addedBitcast) + { + bcatcstr(glsl, ")"); + } + + EndAssignment(psContext, psDest, TO_FLAG_FLOAT, psInst->bSaturate); + bformata(glsl, ";\n"); + } + } +} + +void TranslateAtomicMemOp(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentGLSLString; + ShaderVarType* psVarType = NULL; + uint32_t ui32DataTypeFlag = TO_FLAG_INTEGER; + const char* func = ""; + Operand* dest = 0; + Operand* previousValue = 0; + Operand* destAddr = 0; + Operand* src = 0; + Operand* compare = 0; + + switch (psInst->eOpcode) + { + case OPCODE_IMM_ATOMIC_IADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_IADD\n"); +#endif + func = "atomicAdd"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_IADD\n"); +#endif + func = "atomicAdd"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_AND\n"); +#endif + func = "atomicAnd"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_AND\n"); +#endif + func = "atomicAnd"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_OR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_OR\n"); +#endif + func = "atomicOr"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_OR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_OR\n"); +#endif + func = "atomicOr"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_XOR\n"); +#endif + func = "atomicXor"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_XOR\n"); +#endif + func = "atomicXor"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + + case OPCODE_IMM_ATOMIC_EXCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_EXCH\n"); +#endif + func = "atomicExchange"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_CMP_EXC\n"); +#endif + func = "atomicCompSwap"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + compare = &psInst->asOperands[3]; + src = &psInst->asOperands[4]; + break; + } + case OPCODE_ATOMIC_CMP_STORE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_CMP_STORE\n"); +#endif + func = "atomicCompSwap"; + previousValue = 0; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + compare = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_IMM_ATOMIC_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_UMIN\n"); +#endif + func = "atomicMin"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_UMIN\n"); +#endif + func = "atomicMin"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_IMIN\n"); +#endif + func = "atomicMin"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_IMIN\n"); +#endif + func = "atomicMin"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_UMAX\n"); +#endif + func = "atomicMax"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_UMAX\n"); +#endif + func = "atomicMax"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_IMAX\n"); +#endif + func = "atomicMax"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_IMAX\n"); +#endif + func = "atomicMax"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + } + + AddIndentation(psContext); + + psVarType = LookupStructuredVar(psContext, dest, NULL, 0); + + if (psVarType->Type == SVT_UINT) + { + ui32DataTypeFlag = TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER; + } + else if (psVarType->Type == SVT_INT) + { + ui32DataTypeFlag = TO_FLAG_INTEGER; + } + + if (previousValue) + { + BeginAssignment(psContext, previousValue, ui32DataTypeFlag, psInst->bSaturate); + } + + if (dest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + bcatcstr(glsl, func); + bcatcstr(glsl, "("); + TranslateOperand(psContext, dest, ui32DataTypeFlag & TO_FLAG_NAME_ONLY); + bformata(glsl, "[%d]", 0); + } + else + { + bcatcstr(glsl, func); + bcatcstr(glsl, "("); + TranslateShaderStorageVarName(glsl, psContext->psShader, dest, 1); + bformata(glsl, "["); + TranslateOperand(psContext, destAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + // For some reason the destAddr with the swizzle doesn't translate to an index + // I'm not sure if ".x" is the correct behavior. + bformata(glsl, ".x]"); + } + + if (strcmp(psVarType->Name, "$Element") != 0) + { + bcatcstr(glsl, "."); + ShaderVarName(glsl, psContext->psShader, psVarType->Name); + } + bcatcstr(glsl, ", "); + + if (compare) + { + TranslateOperand(psContext, compare, ui32DataTypeFlag); + bcatcstr(glsl, ", "); + } + + TranslateOperand(psContext, src, ui32DataTypeFlag); + bcatcstr(glsl, ")"); + + if (previousValue) + { + EndAssignment(psContext, previousValue, ui32DataTypeFlag, psInst->bSaturate); + } + + bcatcstr(glsl, ";\n"); +} + +static void TranslateConditional(HLSLCrossCompilerContext* psContext, + Instruction* psInst, + bstring glsl) +{ + const char* statement = ""; + uint32_t bWriteTraceEnd = 0; + if (psInst->eOpcode == OPCODE_BREAKC) + { + statement = "break"; + } + else if (psInst->eOpcode == OPCODE_CONTINUEC) + { + statement = "continue"; + } + else if (psInst->eOpcode == OPCODE_RETC) + { + statement = "return"; + bWriteTraceEnd = (psContext->flags & HLSLCC_FLAG_TRACING_INSTRUMENTATION) != 0; + } + + if (psContext->psShader->ui32MajorVersion < 4) + { + bcatcstr(glsl, "if("); + + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + switch (psInst->eDX9TestType) + { + case D3DSPC_GT: + { + bcatcstr(glsl, " > "); + break; + } + case D3DSPC_EQ: + { + bcatcstr(glsl, " == "); + break; + } + case D3DSPC_GE: + { + bcatcstr(glsl, " >= "); + break; + } + case D3DSPC_LT: + { + bcatcstr(glsl, " < "); + break; + } + case D3DSPC_NE: + { + bcatcstr(glsl, " != "); + break; + } + case D3DSPC_LE: + { + bcatcstr(glsl, " <= "); + break; + } + case D3DSPC_BOOLEAN: + { + bcatcstr(glsl, " != 0"); + break; + } + default: + { + break; + } + } + + if (psInst->eDX9TestType != D3DSPC_BOOLEAN) + { + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + } + + if (psInst->eOpcode != OPCODE_IF && !bWriteTraceEnd) + { + bformata(glsl, "){ %s; }\n", statement); + } + else + { + bcatcstr(glsl, "){\n"); + } + } + else + { + if (psInst->eBooleanTestType == INSTRUCTION_TEST_ZERO) + { + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + + if (psInst->eOpcode != OPCODE_IF && !bWriteTraceEnd) + { + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + bformata(glsl, ")==uint(0){%s;}\n", statement); // Adreno can't handle 0u (it's treated as int) + } + else + { + bformata(glsl, ")==0){%s;}\n", statement); + } + } + else + { + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + bcatcstr(glsl, ")==uint(0){\n"); // Adreno can't handle 0u (it's treated as int) + } + else + { + bcatcstr(glsl, ")==0){\n"); + } + } + } + else + { + ASSERT(psInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO); + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + + if (psInst->eOpcode != OPCODE_IF && !bWriteTraceEnd) + { + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + bformata(glsl, ")!=uint(0)){%s;}\n", statement); // Adreno can't handle 0u (it's treated as int) + } + else + { + bformata(glsl, ")!=0){%s;}\n", statement); + } + } + else + { + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + bcatcstr(glsl, ")!=uint(0)){\n"); // Adreno can't handle 0u (it's treated as int) + } + else + { + bcatcstr(glsl, ")!=0){\n"); + } + } + } + } + + if (bWriteTraceEnd) + { + ASSERT(*psContext->currentGLSLString == glsl); + ++psContext->indent; + WriteEndTrace(psContext); + AddIndentation(psContext); + bformata(glsl, "%s;\n", statement); + AddIndentation(psContext); + --psContext->indent; + bcatcstr(glsl, "}\n"); + } +} + +void UpdateCommonTempVecType(SHADER_VARIABLE_TYPE* peCommonTempVecType, SHADER_VARIABLE_TYPE eNewType) +{ + if (*peCommonTempVecType == SVT_FORCE_DWORD) + { + *peCommonTempVecType = eNewType; + } + else if (*peCommonTempVecType != eNewType) + { + *peCommonTempVecType = SVT_VOID; + } +} + +bool IsFloatType(SHADER_VARIABLE_TYPE type) +{ + switch (type) + { + case SVT_FLOAT: + case SVT_FLOAT10: + case SVT_FLOAT16: + return true; + default: + return false; + } +} + +void SetDataTypes(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount, SHADER_VARIABLE_TYPE* aeCommonTempVecType) +{ + int32_t i; + + SHADER_VARIABLE_TYPE aeTempVecType[MAX_TEMP_VEC4 * 4]; + + for (i = 0; i < MAX_TEMP_VEC4 * 4; ++i) + { + aeTempVecType[i] = SVT_FLOAT; + } + if (aeCommonTempVecType != NULL) + { + for (i = 0; i < MAX_TEMP_VEC4; ++i) + { + aeCommonTempVecType[i] = SVT_FORCE_DWORD; + } + } + + for (i = 0; i < i32InstCount; ++i, psInst++) + { + int k = 0; + + if (psInst->ui32NumOperands == 0) + { + continue; + } + + //Preserve the current type on dest array index + if (psInst->asOperands[0].eType == OPERAND_TYPE_INDEXABLE_TEMP) + { + Operand* psSubOperand = psInst->asOperands[0].psSubOperand[1]; + if (psSubOperand != 0) + { + const uint32_t ui32RegIndex = psSubOperand->ui32RegisterNumber * 4; + ASSERT(psSubOperand->eType == OPERAND_TYPE_TEMP); + + if (psSubOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psSubOperand->aeDataType[psSubOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psSubOperand->aui32Swizzle[0]]; + } + else if (psSubOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psSubOperand->ui32Swizzle == (NO_SWIZZLE)) + { + psSubOperand->aeDataType[0] = aeTempVecType[ui32RegIndex]; + psSubOperand->aeDataType[1] = aeTempVecType[ui32RegIndex]; + psSubOperand->aeDataType[2] = aeTempVecType[ui32RegIndex]; + psSubOperand->aeDataType[3] = aeTempVecType[ui32RegIndex]; + } + else + { + psSubOperand->aeDataType[psSubOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psSubOperand->aui32Swizzle[0]]; + } + } + else if (psSubOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + int c = 0; + uint32_t ui32CompMask = psSubOperand->ui32CompMask; + if (!psSubOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + for (; c < 4; ++c) + { + if (ui32CompMask & (1 << c)) + { + psSubOperand->aeDataType[c] = aeTempVecType[ui32RegIndex + c]; + } + } + } + } + } + + //Preserve the current type on sources. + for (k = psInst->ui32NumOperands - 1; k >= (int)psInst->ui32FirstSrc; --k) + { + int32_t subOperand; + Operand* psOperand = &psInst->asOperands[k]; + + if (psOperand->eType == OPERAND_TYPE_TEMP) + { + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + psOperand->aeDataType[0] = aeTempVecType[ui32RegIndex]; + psOperand->aeDataType[1] = aeTempVecType[ui32RegIndex]; + psOperand->aeDataType[2] = aeTempVecType[ui32RegIndex]; + psOperand->aeDataType[3] = aeTempVecType[ui32RegIndex]; + } + else + { + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + int c = 0; + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + for (; c < 4; ++c) + { + if (ui32CompMask & (1 << c)) + { + psOperand->aeDataType[c] = aeTempVecType[ui32RegIndex + c]; + } + } + } + } + + for (subOperand = 0; subOperand < MAX_SUB_OPERANDS; subOperand++) + { + if (psOperand->psSubOperand[subOperand] != 0) + { + Operand* psSubOperand = psOperand->psSubOperand[subOperand]; + if (psSubOperand->eType == OPERAND_TYPE_TEMP) + { + const uint32_t ui32RegIndex = psSubOperand->ui32RegisterNumber * 4; + + if (psSubOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psSubOperand->aeDataType[psSubOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psSubOperand->aui32Swizzle[0]]; + } + else if (psSubOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psSubOperand->ui32Swizzle == (NO_SWIZZLE)) + { + psSubOperand->aeDataType[0] = aeTempVecType[ui32RegIndex]; + psSubOperand->aeDataType[1] = aeTempVecType[ui32RegIndex]; + psSubOperand->aeDataType[2] = aeTempVecType[ui32RegIndex]; + psSubOperand->aeDataType[3] = aeTempVecType[ui32RegIndex]; + } + else + { + psSubOperand->aeDataType[psSubOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psSubOperand->aui32Swizzle[0]]; + } + } + else if (psSubOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + int c = 0; + uint32_t ui32CompMask = psSubOperand->ui32CompMask; + if (!psSubOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + + for (; c < 4; ++c) + { + if (ui32CompMask & (1 << c)) + { + psSubOperand->aeDataType[c] = aeTempVecType[ui32RegIndex + c]; + } + } + } + } + } + } + } + + SHADER_VARIABLE_TYPE eNewType = SVT_FORCE_DWORD; + + switch (psInst->eOpcode) + { + case OPCODE_RESINFO: + { + if (psInst->eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) + { + eNewType = SVT_INT; + } + else + { + eNewType = SVT_FLOAT; + } + break; + } + case OPCODE_AND: + case OPCODE_OR: + case OPCODE_XOR: + case OPCODE_NOT: + { + eNewType = SVT_UINT; + break; + } + case OPCODE_IADD: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + { + eNewType = SVT_UINT; + + //If the rhs evaluates to signed then that is the dest type picked. + for (uint32_t kk = psInst->ui32FirstSrc; kk < psInst->ui32NumOperands; ++kk) + { + if (GetOperandDataType(psContext, &psInst->asOperands[kk]) == SVT_INT || + psInst->asOperands[kk].eModifier == OPERAND_MODIFIER_NEG || + psInst->asOperands[kk].eModifier == OPERAND_MODIFIER_ABSNEG) + { + eNewType = SVT_INT; + break; + } + } + + break; + } + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { + Operand* dest = &psInst->asOperands[1]; + ShaderVarType* type = LookupStructuredVar(psContext, dest, NULL, 0); + eNewType = type->Type; + break; + } + + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_INE: + case OPCODE_EQ: + case OPCODE_GE: + case OPCODE_LT: + case OPCODE_NE: + case OPCODE_UDIV: + case OPCODE_ULT: + case OPCODE_UGE: + case OPCODE_UMUL: + case OPCODE_UMAD: + case OPCODE_UMAX: + case OPCODE_UMIN: + case OPCODE_USHR: + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + { + if (psContext->psShader->ui32MajorVersion < 4) + { + //SLT and SGE are translated to LT and GE respectively. + //But SLT and SGE have a floating point 1.0f or 0.0f result + //instead of setting all bits on or all bits off. + eNewType = SVT_FLOAT; + } + else + { + eNewType = SVT_UINT; + } + break; + } + + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_D: + case OPCODE_SAMPLE_B: + case OPCODE_LD: + case OPCODE_LD_MS: + case OPCODE_LD_UAV_TYPED: + { + ResourceBinding* psRes = NULL; + if (psInst->eOpcode == OPCODE_LD_UAV_TYPED) + { + GetResourceFromBindingPoint(RGROUP_UAV, psInst->asOperands[2].ui32RegisterNumber, &psContext->psShader->sInfo, &psRes); + } + else + { + GetResourceFromBindingPoint(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, &psContext->psShader->sInfo, &psRes); + } + switch (psRes->ui32ReturnType) + { + case RETURN_TYPE_SINT: + eNewType = SVT_INT; + break; + case RETURN_TYPE_UINT: + eNewType = SVT_UINT; + break; + case RETURN_TYPE_FLOAT: + eNewType = SVT_FLOAT; + break; + default: + ASSERT(0); + break; + } + break; + } + + case OPCODE_MOV: + { + //Inherit the type of the source operand + const Operand* psOperand = &psInst->asOperands[0]; + if (psOperand->eType == OPERAND_TYPE_TEMP) + { + eNewType = GetOperandDataType(psContext, &psInst->asOperands[1]); + } + else + { + continue; + } + break; + } + case OPCODE_MOVC: + { + //Inherit the type of the source operand + const Operand* psOperand = &psInst->asOperands[0]; + if (psOperand->eType == OPERAND_TYPE_TEMP) + { + eNewType = GetOperandDataType(psContext, &psInst->asOperands[2]); + //Check assumption that both the values which MOVC might pick have the same basic data type. + if (!psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING) + { + ASSERT(GetOperandDataType(psContext, &psInst->asOperands[2]) == GetOperandDataType(psContext, &psInst->asOperands[3])); + } + } + else + { + continue; + } + break; + } + case OPCODE_FTOI: + { + ASSERT(IsFloatType(GetOperandDataType(psContext, &psInst->asOperands[1])) || + GetOperandDataType(psContext, &psInst->asOperands[1]) == SVT_VOID); + eNewType = SVT_INT; + break; + } + case OPCODE_FTOU: + { + ASSERT(IsFloatType(GetOperandDataType(psContext, &psInst->asOperands[1])) || + GetOperandDataType(psContext, &psInst->asOperands[1]) == SVT_VOID); + eNewType = SVT_UINT; + break; + } + + case OPCODE_UTOF: + case OPCODE_ITOF: + { + eNewType = SVT_FLOAT; + break; + } + case OPCODE_IF: + case OPCODE_SWITCH: + case OPCODE_BREAKC: + { + const Operand* psOperand = &psInst->asOperands[0]; + if (psOperand->eType == OPERAND_TYPE_TEMP) + { + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + eNewType = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + eNewType = aeTempVecType[ui32RegIndex]; + } + else + { + eNewType = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + for (; k < 4; ++k) + { + if (ui32CompMask & (1 << k)) + { + eNewType = aeTempVecType[ui32RegIndex + k]; + } + } + } + } + else + { + continue; + } + break; + } + case OPCODE_DADD: + { + eNewType = SVT_DOUBLE; + break; + } + case OPCODE_STORE_RAW: + { + eNewType = SVT_FLOAT; + break; + } + default: + { + eNewType = SVT_FLOAT; + break; + } + } + + if (eNewType == SVT_UINT && HaveUVec(psContext->psShader->eTargetLanguage) == 0) + { + //Fallback to signed int if unsigned int is not supported. + eNewType = SVT_INT; + } + + //Process the destination last in order to handle instructions + //where the destination register is also used as a source. + for (k = 0; k < (int)psInst->ui32FirstSrc; ++k) + { + Operand* psOperand = &psInst->asOperands[k]; + if (psOperand->eType == OPERAND_TYPE_TEMP) + { + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + if (HavePrecisionQualifers(psContext->psShader->eTargetLanguage)) + { + switch (psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + break; + case OPERAND_MIN_PRECISION_SINT_16: + eNewType = SVT_INT16; + break; + case OPERAND_MIN_PRECISION_UINT_16: + eNewType = SVT_UINT16; + break; + case OPERAND_MIN_PRECISION_FLOAT_2_8: + eNewType = SVT_FLOAT10; + break; + case OPERAND_MIN_PRECISION_FLOAT_16: + eNewType = SVT_FLOAT16; + break; + default: + break; + } + } + + if (aeCommonTempVecType != NULL) + { + UpdateCommonTempVecType(aeCommonTempVecType + psOperand->ui32RegisterNumber, eNewType); + } + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]] = eNewType; + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = eNewType; + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + aeTempVecType[ui32RegIndex] = eNewType; + psOperand->aeDataType[0] = eNewType; + psOperand->aeDataType[1] = eNewType; + psOperand->aeDataType[2] = eNewType; + psOperand->aeDataType[3] = eNewType; + } + else + { + aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]] = eNewType; + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = eNewType; + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + int c = 0; + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + for (; c < 4; ++c) + { + if (ui32CompMask & (1 << c)) + { + aeTempVecType[ui32RegIndex + c] = eNewType; + psOperand->aeDataType[c] = eNewType; + } + } + } + } + } + ASSERT(eNewType != SVT_FORCE_DWORD); + } +} + +void TranslateInstruction(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentGLSLString; + +#ifdef _DEBUG + AddIndentation(psContext); + bformata(glsl, "//Instruction %d\n", psInst->id); +#if 0 + if (psInst->id == 73) + { + ASSERT(1); //Set breakpoint here to debug an instruction from its ID. + } +#endif +#endif + + switch (psInst->eOpcode) + { + case OPCODE_FTOI: //Fall-through to MOV + case OPCODE_FTOU: //Fall-through to MOV + case OPCODE_MOV: + { + uint32_t srcCount = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t dstCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t ui32DstFlags = TO_FLAG_NONE; + + if (psInst->eOpcode == OPCODE_FTOU) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FTOU\n"); +#endif + ui32DstFlags |= TO_FLAG_UNSIGNED_INTEGER; + + ASSERT(IsFloatType(GetOperandDataType(psContext, &psInst->asOperands[1]))); + } + else if (psInst->eOpcode == OPCODE_FTOI) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FTOI\n"); +#endif + ui32DstFlags |= TO_FLAG_INTEGER; + + ASSERT(IsFloatType(GetOperandDataType(psContext, &psInst->asOperands[1]))); + } + else + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MOV\n"); +#endif + } + + if (psInst->eOpcode == OPCODE_FTOU) + { + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32DstFlags, psInst->bSaturate); + + if (srcCount == 1) + { + bcatcstr(glsl, "uint("); + } + if (srcCount == 2) + { + bcatcstr(glsl, "uvec2("); + } + if (srcCount == 3) + { + bcatcstr(glsl, "uvec3("); + } + if (srcCount == 4) + { + bcatcstr(glsl, "uvec4("); + } + + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + if (srcCount != dstCount) + { + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32DstFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + else + { + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], ui32DstFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + } + else + if (psInst->eOpcode == OPCODE_FTOI) + { + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32DstFlags, psInst->bSaturate); + + if (srcCount == 1) + { + bcatcstr(glsl, "int("); + } + if (srcCount == 2) + { + bcatcstr(glsl, "ivec2("); + } + if (srcCount == 3) + { + bcatcstr(glsl, "ivec3("); + } + if (srcCount == 4) + { + bcatcstr(glsl, "ivec4("); + } + + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + + if (srcCount != dstCount) + { + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32DstFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + else + { + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], ui32DstFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + } + else + { + AddMOVBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[1], 0, psInst->bSaturate); + } + break; + } + case OPCODE_ITOF: //signed to float + case OPCODE_UTOF: //unsigned to float + { +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_ITOF) + { + bcatcstr(glsl, "//ITOF\n"); + } + else + { + bcatcstr(glsl, "//UTOF\n"); + } +#endif + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "vec4("); + TranslateOperand(psContext, &psInst->asOperands[1], (psInst->eOpcode == OPCODE_ITOF) ? TO_FLAG_INTEGER : TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_MAD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MAD\n"); +#endif + CallTernaryOp(psContext, "*", "+", psInst, 0, 1, 2, 3, TO_FLAG_FLOAT); + break; + } + case OPCODE_IMAD: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMAD\n"); +#endif + + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + + CallTernaryOp(psContext, "*", "+", psInst, 0, 1, 2, 3, ui32Flags); + break; + } + case OPCODE_DADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DADD\n"); +#endif + CallBinaryOp(psContext, "+", psInst, 0, 1, 2, TO_FLAG_DOUBLE); + break; + } + case OPCODE_IADD: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IADD\n"); +#endif + //Is this a signed or unsigned add? + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + CallBinaryOp(psContext, "+", psInst, 0, 1, 2, ui32Flags); + break; + } + case OPCODE_ADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ADD\n"); +#endif + CallBinaryOp(psContext, "+", psInst, 0, 1, 2, TO_FLAG_FLOAT); + break; + } + case OPCODE_OR: + { + /*Todo: vector version */ +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//OR\n"); +#endif + CallBinaryOp(psContext, "|", psInst, 0, 1, 2, TO_FLAG_INTEGER); + break; + } + case OPCODE_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//AND\n"); +#endif + CallBinaryOp(psContext, "&", psInst, 0, 1, 2, TO_FLAG_INTEGER); + break; + } + case OPCODE_GE: + { + /* + dest = vec4(greaterThanEqual(vec4(srcA), vec4(srcB)); + Caveat: The result is a boolean but HLSL asm returns 0xFFFFFFFF/0x0 instead. + */ +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GE\n"); +#endif + AddComparision(psContext, psInst, CMP_GE, TO_FLAG_FLOAT); + break; + } + case OPCODE_MUL: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MUL\n"); +#endif + CallBinaryOp(psContext, "*", psInst, 0, 1, 2, TO_FLAG_FLOAT); + break; + } + case OPCODE_IMUL: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMUL\n"); +#endif + if (GetOperandDataType(psContext, &psInst->asOperands[1]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + + ASSERT(psInst->asOperands[0].eType == OPERAND_TYPE_NULL); + + CallBinaryOp(psContext, "*", psInst, 1, 2, 3, ui32Flags); + break; + } + case OPCODE_UDIV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//UDIV\n"); +#endif + //destQuotient, destRemainder, src0, src1 + CallBinaryOp(psContext, "/", psInst, 0, 2, 3, TO_FLAG_UNSIGNED_INTEGER); + CallBinaryOp(psContext, "%", psInst, 1, 2, 3, TO_FLAG_UNSIGNED_INTEGER); + break; + } + case OPCODE_DIV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DIV\n"); +#endif + CallBinaryOp(psContext, "/", psInst, 0, 1, 2, TO_FLAG_FLOAT); + break; + } + case OPCODE_SINCOS: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SINCOS\n"); +#endif + if (psInst->asOperands[0].eType != OPERAND_TYPE_NULL) + { + CallHelper1(psContext, "sin", psInst, 0, 2); + } + + if (psInst->asOperands[1].eType != OPERAND_TYPE_NULL) + { + CallHelper1(psContext, "cos", psInst, 1, 2); + } + break; + } + + case OPCODE_DP2: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP2\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "vec4(dot(("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + bcatcstr(glsl, ").xy, ("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bcatcstr(glsl, ").xy))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_DP3: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP3\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "vec4(dot(("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + bcatcstr(glsl, ").xyz, ("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bcatcstr(glsl, ").xyz))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_DP4: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP4\n"); +#endif + CallHelper2(psContext, "dot", psInst, 0, 1, 2); + break; + } + case OPCODE_INE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INE\n"); +#endif + AddComparision(psContext, psInst, CMP_NE, TO_FLAG_INTEGER); + break; + } + case OPCODE_NE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//NE\n"); +#endif + AddComparision(psContext, psInst, CMP_NE, TO_FLAG_FLOAT); + break; + } + case OPCODE_IGE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IGE\n"); +#endif + AddComparision(psContext, psInst, CMP_GE, TO_FLAG_INTEGER); + break; + } + case OPCODE_ILT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ILT\n"); +#endif + AddComparision(psContext, psInst, CMP_LT, TO_FLAG_INTEGER); + break; + } + case OPCODE_LT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LT\n"); +#endif + AddComparision(psContext, psInst, CMP_LT, TO_FLAG_FLOAT); + break; + } + case OPCODE_IEQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IEQ\n"); +#endif + AddComparision(psContext, psInst, CMP_EQ, TO_FLAG_INTEGER); + break; + } + case OPCODE_ULT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ULT\n"); +#endif + AddComparision(psContext, psInst, CMP_LT, TO_FLAG_UNSIGNED_INTEGER); + break; + } + case OPCODE_UGE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//UGE\n"); +#endif + AddComparision(psContext, psInst, CMP_GE, TO_FLAG_UNSIGNED_INTEGER); + break; + } + case OPCODE_MOVC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MOVC\n"); +#endif + AddMOVCBinaryOp(psContext, &psInst->asOperands[0], 0, &psInst->asOperands[1], &psInst->asOperands[2], &psInst->asOperands[3]); + break; + } + case OPCODE_SWAPC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SWAPC\n"); +#endif + AddMOVCBinaryOp(psContext, &psInst->asOperands[0], 1, &psInst->asOperands[2], &psInst->asOperands[4], &psInst->asOperands[3]); + AddMOVCBinaryOp(psContext, &psInst->asOperands[1], 0, &psInst->asOperands[2], &psInst->asOperands[3], &psInst->asOperands[4]); + AddMOVBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[0], 1, 0); + break; + } + + case OPCODE_LOG: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LOG\n"); +#endif + CallHelper1(psContext, "log2", psInst, 0, 1); + break; + } + case OPCODE_RSQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RSQ\n"); +#endif + CallHelper1(psContext, "inversesqrt", psInst, 0, 1); + break; + } + case OPCODE_EXP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EXP\n"); +#endif + CallHelper1(psContext, "exp2", psInst, 0, 1); + break; + } + case OPCODE_SQRT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SQRT\n"); +#endif + CallHelper1(psContext, "sqrt", psInst, 0, 1); + break; + } + case OPCODE_ROUND_PI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_PI\n"); +#endif + CallHelper1(psContext, "ceil", psInst, 0, 1); + break; + } + case OPCODE_ROUND_NI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_NI\n"); +#endif + CallHelper1(psContext, "floor", psInst, 0, 1); + break; + } + case OPCODE_ROUND_Z: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_Z\n"); +#endif + CallHelper1(psContext, "trunc", psInst, 0, 1); + break; + } + case OPCODE_ROUND_NE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_NE\n"); +#endif + CallHelper1(psContext, "roundEven", psInst, 0, 1); + break; + } + case OPCODE_FRC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FRC\n"); +#endif + CallHelper1(psContext, "fract", psInst, 0, 1); + break; + } + case OPCODE_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMAX\n"); +#endif + CallHelper2Int(psContext, "max", psInst, 0, 1, 2); + break; + } + case OPCODE_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//UMAX\n"); +#endif + CallHelper2UInt(psContext, "max", psInst, 0, 1, 2); + break; + } + case OPCODE_MAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MAX\n"); +#endif + CallHelper2(psContext, "max", psInst, 0, 1, 2); + break; + } + case OPCODE_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMIN\n"); +#endif + CallHelper2Int(psContext, "min", psInst, 0, 1, 2); + break; + } + case OPCODE_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//UMIN\n"); +#endif + CallHelper2UInt(psContext, "min", psInst, 0, 1, 2); + break; + } + case OPCODE_MIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MIN\n"); +#endif + CallHelper2(psContext, "min", psInst, 0, 1, 2); + break; + } + case OPCODE_GATHER4: + { + //dest, coords, tex, sampler + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + const uint32_t ui32SampleToFlags = GetResourceReturnTypeToFlags(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, psContext); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4\n"); +#endif + //gather4 r7.xyzw, r3.xyxx, t3.xyzw, s0.x + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32SampleToFlags, psInst->bSaturate); + bcatcstr(glsl, "(textureGather("); + + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 0); + bcatcstr(glsl, ", "); + TranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(glsl, ")"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + bcatcstr(glsl, ")"); + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32SampleToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_GATHER4_PO_C: + { + //dest, coords, offset, tex, sampler, srcReferenceValue + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[3].ui32RegisterNumber]; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4_PO_C\n"); +#endif + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "(textureGatherOffset("); + + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber, 1); + + bcatcstr(glsl, ", "); + + TranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[5], TO_FLAG_NONE); + + bcatcstr(glsl, ", ivec2("); + //ivec2 offset + psInst->asOperands[2].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[2].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, "))"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[3]); + bcatcstr(glsl, ")"); + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_GATHER4_PO: + { + //dest, coords, offset, tex, sampler + const uint32_t ui32SampleToFlags = GetResourceReturnTypeToFlags(RGROUP_TEXTURE, psInst->asOperands[3].ui32RegisterNumber, psContext); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4_PO\n"); +#endif + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32SampleToFlags, psInst->bSaturate); + bcatcstr(glsl, "(textureGatherOffset("); + + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber, 0); + + bcatcstr(glsl, ", "); + //Texture coord cannot be vec4 + //Determining if it is a vec3 for vec2 yet to be done. + psInst->asOperands[1].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[1].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + + bcatcstr(glsl, ", ivec2("); + //ivec2 offset + psInst->asOperands[2].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[2].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, "))"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[3]); + bcatcstr(glsl, ")"); + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32SampleToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_GATHER4_C: + { + //dest, coords, tex, sampler srcReferenceValue +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4_C\n"); +#endif + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "(textureGather("); + + TextureName(*psContext->currentGLSLString, psContext->psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1); + + bcatcstr(glsl, ", "); + //Texture coord cannot be vec4 + //Determining if it is a vec3 for vec2 yet to be done. + psInst->asOperands[1].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[1].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_NONE); + bcatcstr(glsl, ")"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + bcatcstr(glsl, ")"); + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_SAMPLE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE\n"); +#endif + TranslateTextureSample(psContext, psInst, TEXSMP_FLAG_NONE); + break; + } + case OPCODE_SAMPLE_L: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_L\n"); +#endif + TranslateTextureSample(psContext, psInst, TEXSMP_FLAG_LOD); + break; + } + case OPCODE_SAMPLE_C: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_C\n"); +#endif + + TranslateTextureSample(psContext, psInst, TEXSMP_FLAG_COMPARE); + break; + } + case OPCODE_SAMPLE_C_LZ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_C_LZ\n"); +#endif + + TranslateTextureSample(psContext, psInst, TEXSMP_FLAG_COMPARE | TEXSMP_FLAG_FIRSTLOD); + break; + } + case OPCODE_SAMPLE_D: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_D\n"); +#endif + + TranslateTextureSample(psContext, psInst, TEXSMP_FLAGS_GRAD); + break; + } + case OPCODE_SAMPLE_B: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_B\n"); +#endif + + TranslateTextureSample(psContext, psInst, TEXSMP_FLAG_BIAS); + break; + } + case OPCODE_RET: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RET\n"); +#endif + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); +#endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); +#endif + } + if (psContext->flags & HLSLCC_FLAG_TRACING_INSTRUMENTATION) + { + WriteEndTrace(psContext); + } + AddIndentation(psContext); + bcatcstr(glsl, "return;\n"); + break; + } + case OPCODE_INTERFACE_CALL: + { + const char* name; + ShaderVar* psVar; + uint32_t varFound; + + uint32_t funcPointer; + uint32_t funcTableIndex; + uint32_t funcTable; + uint32_t funcBodyIndex; + uint32_t funcBody; + uint32_t ui32NumBodiesPerTable; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INTERFACE_CALL\n"); +#endif + + ASSERT(psInst->asOperands[0].eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32); + + funcPointer = psInst->asOperands[0].aui32ArraySizes[0]; + funcTableIndex = psInst->asOperands[0].aui32ArraySizes[1]; + funcBodyIndex = psInst->ui32FuncIndexWithinInterface; + + ui32NumBodiesPerTable = psContext->psShader->funcPointer[funcPointer].ui32NumBodiesPerTable; + + funcTable = psContext->psShader->funcPointer[funcPointer].aui32FuncTables[funcTableIndex]; + + funcBody = psContext->psShader->funcTable[funcTable].aui32FuncBodies[funcBodyIndex]; + + varFound = GetInterfaceVarFromOffset(funcPointer, &psContext->psShader->sInfo, &psVar); + + ASSERT(varFound); + + name = &psVar->sType.Name[0]; + + AddIndentation(psContext); + bcatcstr(glsl, name); + TranslateOperandIndexMAD(psContext, &psInst->asOperands[0], 1, ui32NumBodiesPerTable, funcBodyIndex); + //bformata(glsl, "[%d]", funcBodyIndex); + bcatcstr(glsl, "();\n"); + break; + } + case OPCODE_LABEL: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LABEL\n"); +#endif + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); //Closing brace ends the previous function. + AddIndentation(psContext); + + bcatcstr(glsl, "subroutine(SubroutineType)\n"); + bcatcstr(glsl, "void "); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, "(){\n"); + ++psContext->indent; + break; + } + case OPCODE_COUNTBITS: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//COUNTBITS\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "bitCount("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_FIRSTBIT_HI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FIRSTBIT_HI\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "findMSB("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_FIRSTBIT_LO: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FIRSTBIT_LO\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "findLSB("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_FIRSTBIT_SHI: //signed high + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FIRSTBIT_SHI\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "findMSB("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_BFREV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BFREV\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "bitfieldReverse("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_BFI: + { + uint32_t numelements_width = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t numelements_offset = GetNumSwizzleElements(&psInst->asOperands[2]); + uint32_t numelements_dest = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t numoverall_elements = min(min(numelements_width, numelements_offset), numelements_dest); + uint32_t i, j; + static const char* bfi_elementidx[] = { "x", "y", "z", "w" }; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BFI\n"); +#endif + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bformata(glsl, "ivec%d(", numoverall_elements); + for (i = 0; i < numoverall_elements; ++i) + { + bcatcstr(glsl, "bitfieldInsert("); + + for (j = 4; j >= 1; --j) + { + uint32_t opSwizzleCount = GetNumSwizzleElements(&psInst->asOperands[j]); + + if (opSwizzleCount != 1) + { + bcatcstr(glsl, " ("); + } + TranslateOperand(psContext, &psInst->asOperands[j], TO_FLAG_INTEGER); + if (opSwizzleCount != 1) + { + bformata(glsl, " ).%s", bfi_elementidx[i]); + } + if (j != 1) + { + bcatcstr(glsl, ","); + } + } + + bcatcstr(glsl, ") "); + if (i + 1 != numoverall_elements) + { + bcatcstr(glsl, ", "); + } + } + + bcatcstr(glsl, ")."); + for (i = 0; i < numoverall_elements; ++i) + { + bformata(glsl, "%s", bfi_elementidx[i]); + } + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_CUT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//CUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EndPrimitive();\n"); + break; + } + case OPCODE_EMIT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMIT\n"); +#endif + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); +#endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); +#endif + AddIndentation(psContext); + } + + AddIndentation(psContext); + bcatcstr(glsl, "EmitVertex();\n"); + break; + } + case OPCODE_EMITTHENCUT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMITTHENCUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EmitVertex();\nEndPrimitive();\n"); + break; + } + + case OPCODE_CUT_STREAM: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//CUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EndStreamPrimitive("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + + break; + } + case OPCODE_EMIT_STREAM: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMIT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EmitStreamVertex("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_EMITTHENCUT_STREAM: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMITTHENCUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EmitStreamVertex("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + bcatcstr(glsl, "EndStreamPrimitive("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_REP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//REP\n"); +#endif + //Need to handle nesting. + //Max of 4 for rep - 'Flow Control Limitations' http://msdn.microsoft.com/en-us/library/windows/desktop/bb219848(v=vs.85).aspx + + AddIndentation(psContext); + bcatcstr(glsl, "RepCounter = ivec4("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_NONE); + bcatcstr(glsl, ").x;\n"); + + AddIndentation(psContext); + bcatcstr(glsl, "while(RepCounter!=0){\n"); + ++psContext->indent; + break; + } + case OPCODE_ENDREP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ENDREP\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "RepCounter--;\n"); + + --psContext->indent; + + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + break; + } + case OPCODE_LOOP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LOOP\n"); +#endif + AddIndentation(psContext); + + if (psInst->ui32NumOperands == 2) + { + //DX9 version + ASSERT(psInst->asOperands[0].eType == OPERAND_TYPE_SPECIAL_LOOPCOUNTER); + bcatcstr(glsl, "for("); + bcatcstr(glsl, "LoopCounter = "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ".y, ZeroBasedCounter = 0;"); + bcatcstr(glsl, "ZeroBasedCounter < "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ".x;"); + + bcatcstr(glsl, "LoopCounter += "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ".z, ZeroBasedCounter++){\n"); + ++psContext->indent; + } + else + { + bcatcstr(glsl, "while(true){\n"); + ++psContext->indent; + } + break; + } + case OPCODE_ENDLOOP: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ENDLOOP\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + break; + } + case OPCODE_BREAK: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BREAK\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "break;\n"); + break; + } + case OPCODE_BREAKC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BREAKC\n"); +#endif + AddIndentation(psContext); + + TranslateConditional(psContext, psInst, glsl); + break; + } + case OPCODE_CONTINUEC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//CONTINUEC\n"); +#endif + AddIndentation(psContext); + + TranslateConditional(psContext, psInst, glsl); + break; + } + case OPCODE_IF: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IF\n"); +#endif + AddIndentation(psContext); + + TranslateConditional(psContext, psInst, glsl); + ++psContext->indent; + break; + } + case OPCODE_RETC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RETC\n"); +#endif + AddIndentation(psContext); + + TranslateConditional(psContext, psInst, glsl); + break; + } + case OPCODE_ELSE: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ELSE\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "} else {\n"); + psContext->indent++; + break; + } + case OPCODE_ENDSWITCH: + case OPCODE_ENDIF: + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "//ENDIF\n"); + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + break; + } + case OPCODE_CONTINUE: + { + AddIndentation(psContext); + bcatcstr(glsl, "continue;\n"); + break; + } + case OPCODE_DEFAULT: + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "default:\n"); + ++psContext->indent; + break; + } + case OPCODE_NOP: + { + break; + } + case OPCODE_SYNC: + { + const uint32_t ui32SyncFlags = psInst->ui32SyncFlags; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SYNC\n"); +#endif + + if (ui32SyncFlags & SYNC_THREADS_IN_GROUP) + { + AddIndentation(psContext); + bcatcstr(glsl, "barrier();\n"); + AddIndentation(psContext); + bcatcstr(glsl, "groupMemoryBarrier();\n"); + } + if (ui32SyncFlags & SYNC_THREAD_GROUP_SHARED_MEMORY) + { + AddIndentation(psContext); + bcatcstr(glsl, "memoryBarrierShared();\n"); + } + if (ui32SyncFlags & (SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP | SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GLOBAL)) + { + AddIndentation(psContext); + bcatcstr(glsl, "memoryBarrier();\n"); + } + break; + } + case OPCODE_SWITCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SWITCH\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "switch(int("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_NONE); + bcatcstr(glsl, ")){\n"); + + psContext->indent += 2; + break; + } + case OPCODE_CASE: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//case\n"); +#endif + AddIndentation(psContext); + + bcatcstr(glsl, "case "); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ":\n"); + + ++psContext->indent; + break; + } + case OPCODE_EQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EQ\n"); +#endif + AddComparision(psContext, psInst, CMP_EQ, TO_FLAG_FLOAT); + break; + } + case OPCODE_USHR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//USHR\n"); +#endif + CallBinaryOp(psContext, ">>", psInst, 0, 1, 2, TO_FLAG_UNSIGNED_INTEGER); + break; + } + case OPCODE_ISHL: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ISHL\n"); +#endif + + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + + CallBinaryOp(psContext, "<<", psInst, 0, 1, 2, ui32Flags); + break; + } + case OPCODE_ISHR: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ISHR\n"); +#endif + + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + + CallBinaryOp(psContext, ">>", psInst, 0, 1, 2, ui32Flags); + break; + } + case OPCODE_LD: + case OPCODE_LD_MS: + { + ResourceBinding* psBinding = 0; + uint32_t ui32FetchTypeToFlags; +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_LD) + { + bcatcstr(glsl, "//LD\n"); + } + else + { + bcatcstr(glsl, "//LD_MS\n"); + } +#endif + + GetResourceFromBindingPoint(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + ui32FetchTypeToFlags = GetReturnTypeToFlags(psBinding->ui32ReturnType); + + const char* fetchFunctionString = psInst->bAddressOffset ? "texelFetchOffset" : "texelFetch"; + switch (psBinding->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + { + //texelFetch(samplerBuffer, int coord, level) + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, fetchFunctionString); + bcatcstr(glsl, "("); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").x, int(("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").w)"); + if (psInst->bAddressOffset) + { + bformata(glsl, ", %d", psInst->iUAddrOffset); + } + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + { + //texelFetch(samplerBuffer, ivec3 coord, level) + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, fetchFunctionString); + bcatcstr(glsl, "("); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").xyz, int(("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").w)"); + if (psInst->bAddressOffset) + { + if (psBinding->eDimension == REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY) + { + bformata(glsl, ", ivec2(%d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset); + } + else + { + bformata(glsl, ", ivec3(%d, %d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset, + psInst->iWAddrOffset); + } + } + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + + if (IsGmemReservedSlot(FBF_ANY, psInst->asOperands[2].ui32RegisterNumber)) // FRAMEBUFFER FETCH + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + } + else + { + bcatcstr(glsl, fetchFunctionString); + bcatcstr(glsl, "("); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").xy, int(("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").w)"); + if (psInst->bAddressOffset) + { + if (psBinding->eDimension == REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY) + { + bformata(glsl, ", int(%d)", psInst->iUAddrOffset); + } + else + { + bformata(glsl, ", ivec2(%d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset); + } + } + bcatcstr(glsl, ")"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + } + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case REFLECT_RESOURCE_DIMENSION_BUFFER: + { + //texelFetch(samplerBuffer, scalar integer coord) + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, "texelFetch("); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").x)"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: + { + //texelFetch(samplerBuffer, ivec2 coord, sample) + + ASSERT(psInst->eOpcode == OPCODE_LD_MS); + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, "texelFetch("); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").xy, int("); + TranslateOperand(psContext, &psInst->asOperands[3], TO_FLAG_INTEGER); + bcatcstr(glsl, "))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + //texelFetch(samplerBuffer, ivec3 coord, sample) + + ASSERT(psInst->eOpcode == OPCODE_LD_MS); + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, "texelFetch("); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", ivec3(("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").xyz), int("); + TranslateOperand(psContext, &psInst->asOperands[3], TO_FLAG_INTEGER); + bcatcstr(glsl, "))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32FetchTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + case REFLECT_RESOURCE_DIMENSION_BUFFEREX: + default: + { + break; + } + } + break; + } + case OPCODE_DISCARD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DISCARD\n"); +#endif + AddIndentation(psContext); + if (psContext->psShader->ui32MajorVersion <= 3) + { + bcatcstr(glsl, "if(any(lessThan(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT); + + if (psContext->psShader->ui32MajorVersion == 1) + { + /* SM1.X only kills based on the rgb channels */ + bcatcstr(glsl, ").xyz, vec3(0.0)))){discard;}\n"); + } + else + { + bcatcstr(glsl, "), vec4(0.0)))){discard;}\n"); + } + } + else if (psInst->eBooleanTestType == INSTRUCTION_TEST_ZERO) + { + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT); + bcatcstr(glsl, ")==0.0){discard;}\n"); + } + else + { + ASSERT(psInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO); + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT); + bcatcstr(glsl, ")!=0.0){discard;}\n"); + } + break; + } + case OPCODE_LOD: + { + uint32_t ui32SampleTypeToFlags = GetResourceReturnTypeToFlags(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, psContext); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LOD\n"); +#endif + //LOD computes the following vector (ClampedLOD, NonClampedLOD, 0, 0) + + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32SampleTypeToFlags, psInst->bSaturate); + + //If the core language does not have query-lod feature, + //then the extension is used. The name of the function + //changed between extension and core. + if (HaveQueryLod(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "textureQueryLod("); + } + else + { + bcatcstr(glsl, "textureQueryLOD("); + } + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ","); + TranslateTexCoord(psContext, + psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber], + &psInst->asOperands[1]); + bcatcstr(glsl, ")"); + + //The swizzle on srcResource allows the returned values to be swizzled arbitrarily before they are written to the destination. + + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + EndAssignment(psContext, &psInst->asOperands[0], ui32SampleTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_EVAL_CENTROID: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EVAL_CENTROID\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "interpolateAtCentroid("); + //interpolateAtCentroid accepts in-qualified variables. + //As long as bytecode only writes vX registers in declarations + //we should be able to use the declared name directly. + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_DECLARATION_NAME); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_EVAL_SAMPLE_INDEX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EVAL_SAMPLE_INDEX\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "interpolateAtSample("); + //interpolateAtSample accepts in-qualified variables. + //As long as bytecode only writes vX registers in declarations + //we should be able to use the declared name directly. + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_DECLARATION_NAME); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_EVAL_SNAPPED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EVAL_SNAPPED\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "interpolateAtOffset("); + //interpolateAtOffset accepts in-qualified variables. + //As long as bytecode only writes vX registers in declarations + //we should be able to use the declared name directly. + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_DECLARATION_NAME); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER); + bcatcstr(glsl, ".xy)"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_LD_STRUCTURED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LD_STRUCTURED "); +#endif + uint32_t reg_num = psInst->asOperands[3].ui32RegisterNumber; + if (reg_num >= GMEM_PLS_RO_SLOT && reg_num <= GMEM_PLS_RW_SLOT) + { +#ifdef _DEBUG + bcatcstr(glsl, "-> LOAD FROM PLS\n"); +#endif + // Ensure it's not a write only PLS + ASSERT(reg_num != GMEM_PLS_WO_SLOT); + + TranslateShaderPLSLoad(psContext, psInst); + } + else + { + bcatcstr(glsl, "\n"); + TranslateShaderStorageLoad(psContext, psInst); + } + break; + } + case OPCODE_LD_UAV_TYPED: + { + uint32_t ui32UAVReturnTypeToFlags = GetResourceReturnTypeToFlags(RGROUP_UAV, psInst->asOperands[2].ui32RegisterNumber, psContext); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LD_UAV_TYPED\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], ui32UAVReturnTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, "imageLoad("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NAME_ONLY); + + switch (psInst->eResDim) + { + case RESOURCE_DIMENSION_BUFFER: + case RESOURCE_DIMENSION_TEXTURE1D: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").x)"); + break; + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + case RESOURCE_DIMENSION_TEXTURE2DMS: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").xy)"); + break; + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case RESOURCE_DIMENSION_TEXTURECUBE: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").xyz)"); + break; + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").xyzw)"); + break; + } + + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + EndAssignment(psContext, &psInst->asOperands[0], ui32UAVReturnTypeToFlags, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_STORE_RAW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//STORE_RAW\n"); +#endif + TranslateShaderStorageStore(psContext, psInst); + break; + } + case OPCODE_STORE_STRUCTURED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//STORE_STRUCTURE "); +#endif + uint32_t reg_num = psInst->asOperands[0].ui32RegisterNumber; + if (reg_num >= GMEM_PLS_RO_SLOT && reg_num <= GMEM_PLS_RW_SLOT) + { +#ifdef _DEBUG + bcatcstr(glsl, "-> STORE TO PLS\n"); +#endif + // Ensure it's not a read only PLS + ASSERT(reg_num != GMEM_PLS_RO_SLOT); + + TranslateShaderPLSStore(psContext, psInst); + } + else + { + bcatcstr(glsl, "\n"); + TranslateShaderStorageStore(psContext, psInst); + } + break; + } + + case OPCODE_STORE_UAV_TYPED: + { + ResourceBinding* psRes; + int foundResource; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//STORE_UAV_TYPED\n"); +#endif + AddIndentation(psContext); + + foundResource = GetResourceFromBindingPoint(RGROUP_UAV, psInst->asOperands[0].ui32RegisterNumber, &psContext->psShader->sInfo, &psRes); + + ASSERT(foundResource); + + bcatcstr(glsl, "imageStore("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_NAME_ONLY); + + switch (psRes->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_BUFFER: + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ").x"); + + // HACK!! + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, ");\n"); + break; + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ".xy)"); + + // HACK!! + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bformata(glsl, ");\n"); + break; + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ".xyz)"); + + // HACK!! + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bformata(glsl, ");\n"); + break; + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ".xyzw)"); + + // HACK!! + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bformata(glsl, ");\n"); + break; + } + + break; + } + case OPCODE_LD_RAW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LD_RAW\n"); +#endif + + TranslateShaderStorageLoad(psContext, psInst); + break; + } + + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMIN: + case OPCODE_ATOMIC_IMAX: + case OPCODE_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { + TranslateAtomicMemOp(psContext, psInst); + break; + } + case OPCODE_UBFE: + case OPCODE_IBFE: + { + const char* swizzles = "xyzw"; + uint32_t eDataType, destElem; + uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t s1ElemCount = GetNumSwizzleElements(&psInst->asOperands[2]); + uint32_t s2ElemCount = GetNumSwizzleElements(&psInst->asOperands[3]); + const char* szVecType; + const char* szDataType; +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_UBFE) + { + bcatcstr(glsl, "//OPCODE_UBFE\n"); + } + else + { + bcatcstr(glsl, "//OPCODE_IBFE\n"); + } +#endif + if (psInst->eOpcode == OPCODE_UBFE) + { + eDataType = TO_FLAG_UNSIGNED_INTEGER; + szVecType = "uvec"; + szDataType = "uint"; + } + else + { + eDataType = TO_FLAG_INTEGER; + szVecType = "ivec"; + szDataType = "int"; + } + + if (psContext->psShader->eTargetLanguage != LANG_ES_300) + { + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], eDataType, psInst->bSaturate); + + if (destElemCount > 1) + { + bformata(glsl, "%s%d(", szVecType, destElemCount); + } + + for (destElem = 0; destElem < destElemCount; ++destElem) + { + if (destElem > 0) + { + bcatcstr(glsl, ", "); + } + + bformata(glsl, "bitfieldExtract("); + + TranslateOperand(psContext, &psInst->asOperands[3], eDataType); + if (s2ElemCount > 1) + { + TranslateOperandSwizzle(psContext, &psInst->asOperands[3]); + bformata(glsl, ".%c", swizzles[destElem]); + } + + bcatcstr(glsl, ", "); + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER); + if (s1ElemCount > 1) + { + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + bformata(glsl, ".%c", swizzles[destElem]); + } + + bcatcstr(glsl, ", "); + + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + if (s0ElemCount > 1) + { + TranslateOperandSwizzle(psContext, &psInst->asOperands[1]); + bformata(glsl, ".%c", swizzles[destElem]); + } + + bformata(glsl, ")"); + } + if (destElemCount > 1) + { + bcatcstr(glsl, ")"); + } + EndAssignment(psContext, &psInst->asOperands[0], eDataType, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + else + { + // Following is the explicit impl' for ES3.0 + // Here's the description of what bitfieldExtract actually does + // https://www.opengl.org/registry/specs/ARB/gpu_shader5.txt + + + AddIndentation(psContext); + bcatcstr(glsl, "{\n"); + + // << (32-bits-offset) + AddIndentation(psContext); + AddIndentation(psContext); + bcatcstr(glsl, "int offsetLeft = (32 - "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, " - "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER); + bcatcstr(glsl, ");\n"); + + // >> (32-bits) + AddIndentation(psContext); + AddIndentation(psContext); + bcatcstr(glsl, "int offsetRight = (32 - "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ");\n"); + + AddIndentation(psContext); + AddIndentation(psContext); + bformata(glsl, "%s tmp;\n", szDataType); + + for (destElem = 0; destElem < destElemCount; ++destElem) + { + AddIndentation(psContext); + AddIndentation(psContext); + bcatcstr(glsl, "tmp = "); + + if (psInst->eOpcode == OPCODE_IBFE) + { + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, " ? "); + } + + TranslateOperand(psContext, &psInst->asOperands[3], eDataType); + if (s2ElemCount > 1) + { + TranslateOperandSwizzle(psContext, &psInst->asOperands[3]); + bformata(glsl, ".%c", swizzles[destElem]); + } + if (psInst->eOpcode == OPCODE_IBFE) + { + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, " : 0 "); + } + bcatcstr(glsl, ";\n"); + + AddIndentation(psContext); + AddIndentation(psContext); + bcatcstr(glsl, "tmp = ((tmp << offsetLeft) >> offsetRight);\n"); + + AddIndentation(psContext); + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], 0, psInst->bSaturate); + if (eDataType == TO_FLAG_INTEGER) + { + bcatcstr(glsl, "intBitsToFloat(tmp));\n"); + } + else + { + bcatcstr(glsl, "uintBitsToFloat(tmp));\n"); + } + } + + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + } + + break; + } + case OPCODE_RCP: + { + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RCP\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "(vec4(1.0) / vec4("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + bcatcstr(glsl, "))"); + AddSwizzleUsingElementCount(psContext, destElemCount); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_F16TOF32: + { + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//F16TOF32\n"); +#endif + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = {".x", ".y", ".z", ".w"}; + + //unpackHalf2x16 converts two f16s packed into uint to two f32s. + + //dest.swiz.x = unpackHalf2x16(src.swiz.x).x + //dest.swiz.y = unpackHalf2x16(src.swiz.y).x + //dest.swiz.z = unpackHalf2x16(src.swiz.z).x + //dest.swiz.w = unpackHalf2x16(src.swiz.w).x + + AddIndentation(psContext); + if (destElemCount > 1) + { + BeginAssignmentEx(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate, swizzle[destElem]); + } + else + { + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + } + + bcatcstr(glsl, "unpackHalf2x16("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + if (s0ElemCount > 1) + { + bcatcstr(glsl, swizzle[destElem]); + } + bcatcstr(glsl, ").x"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + break; + } + case OPCODE_F32TOF16: + { + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//F32TOF16\n"); +#endif + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = {".x", ".y", ".z", ".w"}; + + //packHalf2x16 converts two f32s to two f16s packed into a uint. + + //dest.swiz.x = packHalf2x16(vec2(src.swiz.x)) & 0xFFFF + //dest.swiz.y = packHalf2x16(vec2(src.swiz.y)) & 0xFFFF + //dest.swiz.z = packHalf2x16(vec2(src.swiz.z)) & 0xFFFF + //dest.swiz.w = packHalf2x16(vec2(src.swiz.w)) & 0xFFFF + + AddIndentation(psContext); + if (destElemCount > 1) + { + BeginAssignmentEx(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate, swizzle[destElem]); + } + else + { + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + } + + bcatcstr(glsl, "packHalf2x16(vec2("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + if (s0ElemCount > 1) + { + bcatcstr(glsl, swizzle[destElem]); + } + bcatcstr(glsl, ")) & 0xFFFFu"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + } + break; + } + case OPCODE_INEG: + { + uint32_t dstCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t srcCount = GetNumSwizzleElements(&psInst->asOperands[1]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INEG\n"); +#endif + //dest = 0 - src0 + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + //bcatcstr(glsl, " = 0 - "); + bcatcstr(glsl, "-("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE | TO_FLAG_INTEGER); + if (srcCount > dstCount) + { + AddSwizzleUsingElementCount(psContext, dstCount); + } + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DERIV_RTX\n"); +#endif + CallHelper1(psContext, "dFdx", psInst, 0, 1); + break; + } + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_DERIV_RTY: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DERIV_RTY\n"); +#endif + CallHelper1(psContext, "dFdy", psInst, 0, 1); + break; + } + case OPCODE_LRP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LRP\n"); +#endif + CallHelper3(psContext, "mix", psInst, 0, 2, 3, 1); + break; + } + case OPCODE_DP2ADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP2ADD\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "dot(vec2("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + bcatcstr(glsl, "), vec2("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bcatcstr(glsl, ")) + "); + TranslateOperand(psContext, &psInst->asOperands[3], TO_FLAG_FLOAT); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_POW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//POW\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, "pow(abs("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_FLOAT); + bcatcstr(glsl, "), "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_FLOAT); + bcatcstr(glsl, ")"); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_FLOAT, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + + case OPCODE_IMM_ATOMIC_ALLOC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_ALLOC\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "atomicCounterIncrement("); + bformata(glsl, "UAV%d_counter)", psInst->asOperands[1].ui32RegisterNumber); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_IMM_ATOMIC_CONSUME: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_CONSUME\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, "atomicCounterDecrement("); + bformata(glsl, "UAV%d_counter)", psInst->asOperands[1].ui32RegisterNumber); + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + + case OPCODE_NOT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INOT\n"); +#endif + AddIndentation(psContext); + BeginAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + + uint32_t uDestElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t uSrcElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + + if (uDestElemCount == uSrcElemCount) + { + bcatcstr(glsl, "~("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ")"); + } + else + { + ASSERT(uSrcElemCount > uDestElemCount); + bformata(glsl, "ivec%d(~(", uSrcElemCount); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, "))"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + } + + EndAssignment(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, psInst->bSaturate); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//XOR\n"); +#endif + + CallBinaryOp(psContext, "^", psInst, 0, 1, 2, TO_FLAG_INTEGER); + break; + } + case OPCODE_RESINFO: + { + const RESINFO_RETURN_TYPE eResInfoReturnType = psInst->eResInfoReturnType; + uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RESINFO\n"); +#endif + + //ASSERT(psInst->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + //ASSERT(psInst->asOperands[0].ui32CompMask == OPERAND_4_COMPONENT_MASK_ALL); + + + + + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = {"x", "y", "z", "w"}; + uint32_t ui32ResInfoReturnTypeToFlags = (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) ? TO_FLAG_INTEGER /* currently it's treated as int */ : TO_FLAG_FLOAT; + + AddIndentation(psContext); + if (destElemCount > 1) + { + BeginAssignmentEx(psContext, &psInst->asOperands[0], ui32ResInfoReturnTypeToFlags, psInst->bSaturate, swizzle[destElem]); + } + else + { + BeginAssignment(psContext, &psInst->asOperands[0], ui32ResInfoReturnTypeToFlags, psInst->bSaturate); + } + + GetResInfoData(psContext, psInst, destElem); + + EndAssignment(psContext, &psInst->asOperands[0], ui32ResInfoReturnTypeToFlags, psInst->bSaturate); + + bcatcstr(glsl, ";\n"); + } + + break; + } + + + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DMOV: + case OPCODE_DMOVC: + case OPCODE_DTOF: + case OPCODE_FTOD: + case OPCODE_DDIV: + case OPCODE_DFMA: + case OPCODE_DRCP: + case OPCODE_MSAD: + case OPCODE_DTOI: + case OPCODE_DTOU: + case OPCODE_ITOD: + case OPCODE_UTOD: + default: + { + ASSERT(0); + break; + } + } +} + +static int IsIntegerOpcode(OPCODE_TYPE eOpcode) +{ + switch (eOpcode) + { + case OPCODE_IADD: + case OPCODE_IF: + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INE: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_ITOF: + case OPCODE_AND: + case OPCODE_OR: + { + return 1; + } + default: + { + return 0; + } + } +} + +int InstructionUsesRegister(const Instruction* psInst, const Operand* psOperand) +{ + uint32_t operand; + for (operand = 0; operand < psInst->ui32NumOperands; ++operand) + { + if (psInst->asOperands[operand].eType == psOperand->eType) + { + if (psInst->asOperands[operand].ui32RegisterNumber == psOperand->ui32RegisterNumber) + { + if (CompareOperandSwizzles(&psInst->asOperands[operand], psOperand)) + { + return 1; + } + } + } + } + return 0; +} + +void MarkIntegerImmediates(HLSLCrossCompilerContext* psContext) +{ + const uint32_t count = psContext->psShader->ui32InstCount; + Instruction* psInst = psContext->psShader->psInst; + uint32_t i; + + for (i = 0; i < count; ) + { + if (psInst[i].eOpcode == OPCODE_MOV && psInst[i].asOperands[1].eType == OPERAND_TYPE_IMMEDIATE32 && + psInst[i].asOperands[0].eType == OPERAND_TYPE_TEMP) + { + uint32_t k; + + for (k = i + 1; k < count; ++k) + { + if (psInst[k].eOpcode == OPCODE_ILT) + { + k = k; + } + if (InstructionUsesRegister(&psInst[k], &psInst[i].asOperands[0])) + { + if (IsIntegerOpcode(psInst[k].eOpcode)) + { + psInst[i].asOperands[1].iIntegerImmediate = 1; + } + + goto next_iteration; + } + } + } +next_iteration: + ++i; + } +} diff --git a/Code/Tools/HLSLCrossCompiler/src/toGLSLOperand.c b/Code/Tools/HLSLCrossCompiler/src/toGLSLOperand.c new file mode 100644 index 0000000000..b77576ed51 --- /dev/null +++ b/Code/Tools/HLSLCrossCompiler/src/toGLSLOperand.c @@ -0,0 +1,2121 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/toGLSLDeclaration.h" +#include "internal_includes/hlslccToolkit.h" +#include "internal_includes/languages.h" +#include "bstrlib.h" +#include "hlslcc.h" +#include "internal_includes/debug.h" + +#include +#include +#include + +#if !defined(isnan) +#ifdef _MSC_VER +#define isnan(x) _isnan(x) +#define isinf(x) (!_finite(x)) +#endif +#endif + +#define fpcheck(x) (isnan(x) || isinf(x)) + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); + +// Returns true if types are just different precisions of the same underlying type +static bool AreTypesCompatible(SHADER_VARIABLE_TYPE a, uint32_t ui32TOFlag) +{ + SHADER_VARIABLE_TYPE b = TypeFlagsToSVTType(ui32TOFlag); + + if (a == b) + return true; + + // Special case for array indices: both uint and int are fine + if ((ui32TOFlag & TO_FLAG_INTEGER) && (ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) && + (a == SVT_INT || a == SVT_INT16 || a == SVT_UINT || a == SVT_UINT16)) + return true; + + if ((a == SVT_FLOAT || a == SVT_FLOAT16 || a == SVT_FLOAT10) && + (b == SVT_FLOAT || b == SVT_FLOAT16 || b == SVT_FLOAT10)) + return true; + + if ((a == SVT_INT || a == SVT_INT16 || a == SVT_INT12) && + (b == SVT_INT || b == SVT_INT16 || a == SVT_INT12)) + return true; + + if ((a == SVT_UINT || a == SVT_UINT16) && + (b == SVT_UINT || b == SVT_UINT16)) + return true; + + return false; +} + +int GetMaxComponentFromComponentMask(const Operand* psOperand) +{ + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Comonent Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + return 4; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + return 3; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + return 2; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + return 1; + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + return 4; + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + return 1; + } + } + + return 4; +} + +//Single component repeated +//e..g .wwww +uint32_t IsSwizzleReplacated(const Operand* psOperand) +{ + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == WWWW_SWIZZLE || + psOperand->ui32Swizzle == ZZZZ_SWIZZLE || + psOperand->ui32Swizzle == YYYY_SWIZZLE || + psOperand->ui32Swizzle == XXXX_SWIZZLE) + { + return 1; + } + } + } + return 0; +} + +//e.g. +//.z = 1 +//.x = 1 +//.yw = 2 +uint32_t GetNumSwizzleElements(const Operand* psOperand) +{ + uint32_t count = 0; + + switch (psOperand->eType) + { + case OPERAND_TYPE_IMMEDIATE32: + case OPERAND_TYPE_IMMEDIATE64: + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH: + { + return psOperand->iNumComponents; + } + default: + { + break; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Comonent Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + count++; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + count++; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + count++; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + count++; + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + count++; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + count++; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + count++; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + count++; + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + count++; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + count++; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + count++; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + count++; + } + } + + //Component Select 1 + } + + if (!count) + { + return psOperand->iNumComponents; + } + + return count; +} + +void AddSwizzleUsingElementCount(HLSLCrossCompilerContext* psContext, uint32_t count) +{ + bstring glsl = *psContext->currentGLSLString; + if (count) + { + bcatcstr(glsl, "."); + bcatcstr(glsl, "x"); + count--; + } + if (count) + { + bcatcstr(glsl, "y"); + count--; + } + if (count) + { + bcatcstr(glsl, "z"); + count--; + } + if (count) + { + bcatcstr(glsl, "w"); + count--; + } +} + +uint32_t ConvertOperandSwizzleToComponentMask(const Operand* psOperand) +{ + uint32_t mask = 0; + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Comonent Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + mask = psOperand->ui32CompMask; + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + mask |= OPERAND_4_COMPONENT_MASK_X; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + mask |= OPERAND_4_COMPONENT_MASK_Y; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + mask |= OPERAND_4_COMPONENT_MASK_Z; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + mask |= OPERAND_4_COMPONENT_MASK_W; + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + mask |= OPERAND_4_COMPONENT_MASK_X; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + mask |= OPERAND_4_COMPONENT_MASK_Y; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + mask |= OPERAND_4_COMPONENT_MASK_Z; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + mask |= OPERAND_4_COMPONENT_MASK_W; + } + } + + //Component Select 1 + } + + return mask; +} + +//Non-zero means the components overlap +int CompareOperandSwizzles(const Operand* psOperandA, const Operand* psOperandB) +{ + uint32_t maskA = ConvertOperandSwizzleToComponentMask(psOperandA); + uint32_t maskB = ConvertOperandSwizzleToComponentMask(psOperandB); + + return maskA & maskB; +} + + +void TranslateOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + bstring glsl = *psContext->currentGLSLString; + + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return; + } + } + + if (psOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + /*ConstantBuffer* psCBuf = NULL; + ShaderVar* psVar = NULL; + int32_t index = -1; + GetConstantBufferFromBindingPoint(psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + + //Access the Nth vec4 (N=psOperand->aui32ArraySizes[1]) + //then apply the sizzle. + + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVar, &index); + + bformata(glsl, ".%s", psVar->Name); + if(index != -1) + { + bformata(glsl, "[%d]", index); + }*/ + + //return; + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Comonent Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + bcatcstr(glsl, "."); + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + bcatcstr(glsl, "x"); + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + bcatcstr(glsl, "y"); + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + bcatcstr(glsl, "z"); + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + bcatcstr(glsl, "w"); + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + bcatcstr(glsl, "."); + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + bcatcstr(glsl, "x"); + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + bcatcstr(glsl, "y"); + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + bcatcstr(glsl, "z"); + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + bcatcstr(glsl, "w"); + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + bcatcstr(glsl, "."); + + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + bcatcstr(glsl, "x"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + bcatcstr(glsl, "y"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + bcatcstr(glsl, "z"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + bcatcstr(glsl, "w"); + } + } + + //Component Select 1 + } +} + +int GetFirstOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return -1; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Comonent Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + return 0; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + return 1; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + return 2; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + return 3; + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + return 0; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + return 1; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + return 2; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + return 3; + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + return 0; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + return 1; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + return 2; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + return 3; + } + } + + //Component Select 1 + } + + return -1; +} + +void TranslateOperandIndex(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index) +{ + int i = index; + int isGeoShader = psContext->psShader->eShaderType == GEOMETRY_SHADER ? 1 : 0; + + bstring glsl = *psContext->currentGLSLString; + + ASSERT(index < psOperand->iIndexDims); + + switch (psOperand->eIndexRep[i]) + { + case OPERAND_INDEX_IMMEDIATE32: + { + if (i > 0 || isGeoShader) + { + bformata(glsl, "[%d]", psOperand->aui32ArraySizes[i]); + } + else + { + bformata(glsl, "%d", psOperand->aui32ArraySizes[i]); + } + break; + } + case OPERAND_INDEX_RELATIVE: + { + bcatcstr(glsl, "[int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bcatcstr(glsl, ")]"); + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + bcatcstr(glsl, "[int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bformata(glsl, ") + %d]", psOperand->aui32ArraySizes[i]); + break; + } + default: + { + break; + } + } +} + +void TranslateOperandIndexMAD(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add) +{ + int i = index; + int isGeoShader = psContext->psShader->eShaderType == GEOMETRY_SHADER ? 1 : 0; + + bstring glsl = *psContext->currentGLSLString; + + ASSERT(index < psOperand->iIndexDims); + + switch (psOperand->eIndexRep[i]) + { + case OPERAND_INDEX_IMMEDIATE32: + { + if (i > 0 || isGeoShader) + { + bformata(glsl, "[%d*%d+%d]", psOperand->aui32ArraySizes[i], multiply, add); + } + else + { + bformata(glsl, "%d*%d+%d", psOperand->aui32ArraySizes[i], multiply, add); + } + break; + } + case OPERAND_INDEX_RELATIVE: + { + bcatcstr(glsl, "[int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bformata(glsl, ")*%d+%d]", multiply, add); + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + bcatcstr(glsl, "[(int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bformata(glsl, ") + %d)*%d+%d]", psOperand->aui32ArraySizes[i], multiply, add); + break; + } + default: + { + break; + } + } +} + +void TranslateVariableNameByOperandType(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle) +{ + bstring glsl = *psContext->currentGLSLString; + + switch (psOperand->eType) + { + case OPERAND_TYPE_IMMEDIATE32: + { + if (psOperand->iNumComponents == 1) + { + if (ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) + { + bformata(glsl, "%uu", + *((unsigned int*)(&psOperand->afImmediates[0]))); + } + else + if ((ui32TOFlag & TO_FLAG_INTEGER) || ((ui32TOFlag & TO_FLAG_FLOAT) == 0 && psOperand->iIntegerImmediate) || fpcheck(psOperand->afImmediates[0])) + { + if (ui32TOFlag & TO_FLAG_FLOAT) + { + bcatcstr(glsl, "float"); + } + else if (ui32TOFlag & TO_FLAG_INTEGER) + { + bcatcstr(glsl, "int"); + } + bcatcstr(glsl, "("); + + // yet another Qualcomm's special case + // GLSL compiler thinks that -2147483648 is an integer overflow which is not + if (*((int*)(&psOperand->afImmediates[0])) == 2147483648) + { + bformata(glsl, "-2147483647-1"); + } + else + { + // this is expected to fix paranoid compiler checks such as Qualcomm's + if (*((unsigned int*)(&psOperand->afImmediates[0])) >= 2147483648) + { + bformata(glsl, "%d", + *((int*)(&psOperand->afImmediates[0]))); + } + else + { + bformata(glsl, "%d", + *((int*)(&psOperand->afImmediates[0]))); + } + } + bcatcstr(glsl, ")"); + } + else + { + bformata(glsl, "%e", + psOperand->afImmediates[0]); + } + } + else + { + if (ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) + { + bformata(glsl, "uvec4(%uu, %uu, %uu, %uu)", + *(unsigned int*)&psOperand->afImmediates[0], + *(unsigned int*)&psOperand->afImmediates[1], + *(unsigned int*)&psOperand->afImmediates[2], + *(unsigned int*)&psOperand->afImmediates[3]); + } + else + if ((ui32TOFlag & TO_FLAG_INTEGER) || + ((ui32TOFlag & TO_FLAG_FLOAT) == 0 && psOperand->iIntegerImmediate) || + fpcheck(psOperand->afImmediates[0]) || + fpcheck(psOperand->afImmediates[1]) || + fpcheck(psOperand->afImmediates[2]) || + fpcheck(psOperand->afImmediates[3])) + { + // this is expected to fix paranoid compiler checks such as Qualcomm's + if (ui32TOFlag & TO_FLAG_FLOAT) + { + bcatcstr(glsl, "vec4"); + } + else if (ui32TOFlag & TO_FLAG_INTEGER) + { + bcatcstr(glsl, "ivec4"); + } + else if (ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) + { + bcatcstr(glsl, "uvec4"); + } + bcatcstr(glsl, "("); + + if ((*(unsigned int*)&psOperand->afImmediates[0]) == 2147483648u) + { + bformata(glsl, "int(-2147483647-1), "); + } + else + { + bformata(glsl, "%d, ", *(int*)&psOperand->afImmediates[0]); + } + if ((*(unsigned int*)&psOperand->afImmediates[1]) == 2147483648u) + { + bformata(glsl, "int(-2147483647-1), "); + } + else + { + bformata(glsl, "%d, ", *(int*)&psOperand->afImmediates[1]); + } + if ((*(unsigned int*)&psOperand->afImmediates[2]) == 2147483648u) + { + bformata(glsl, "int(-2147483647-1), "); + } + else + { + bformata(glsl, "%d, ", *(int*)&psOperand->afImmediates[2]); + } + if ((*(unsigned int*)&psOperand->afImmediates[3]) == 2147483648u) + { + bformata(glsl, "int(-2147483647-1)) "); + } + else + { + bformata(glsl, "%d)", *(int*)&psOperand->afImmediates[3]); + } + } + else + { + bformata(glsl, "vec4(%e, %e, %e, %e)", + psOperand->afImmediates[0], + psOperand->afImmediates[1], + psOperand->afImmediates[2], + psOperand->afImmediates[3]); + } + if (psOperand->iNumComponents != 4) + { + AddSwizzleUsingElementCount(psContext, psOperand->iNumComponents); + } + } + break; + } + case OPERAND_TYPE_IMMEDIATE64: + { + if (psOperand->iNumComponents == 1) + { + bformata(glsl, "%e", + psOperand->adImmediates[0]); + } + else + { + bformata(glsl, "dvec4(%e, %e, %e, %e)", + psOperand->adImmediates[0], + psOperand->adImmediates[1], + psOperand->adImmediates[2], + psOperand->adImmediates[3]); + if (psOperand->iNumComponents != 4) + { + AddSwizzleUsingElementCount(psContext, psOperand->iNumComponents); + } + } + break; + } + case OPERAND_TYPE_INPUT: + { + switch (psOperand->iIndexDims) + { + case INDEX_2D: + { + if (psOperand->aui32ArraySizes[1] == 0) //Input index zero - position. + { + bcatcstr(glsl, "gl_in"); + TranslateOperandIndex(psContext, psOperand, TO_FLAG_NONE); //Vertex index + bcatcstr(glsl, ".gl_Position"); + } + else + { + const char* name = "Input"; + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + name = GetDeclaredInputName(psContext, psContext->psShader->eShaderType, psOperand); + } + + bformata(glsl, "%s%d", name, psOperand->aui32ArraySizes[1]); + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + bcstrfree((char*)name); + } + TranslateOperandIndex(psContext, psOperand, TO_FLAG_NONE); //Vertex index + } + break; + } + default: + { + if (psOperand->eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE) + { + bformata(glsl, "Input%d[int(", psOperand->ui32RegisterNumber); + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")]"); + } + else + { + if (psContext->psShader->aIndexedInput[psOperand->ui32RegisterNumber] != 0) + { + const uint32_t parentIndex = psContext->psShader->aIndexedInputParents[psOperand->ui32RegisterNumber]; + bformata(glsl, "Input%d[%d]", parentIndex, + psOperand->ui32RegisterNumber - parentIndex); + } + else + { + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + char* name = GetDeclaredInputName(psContext, psContext->psShader->eShaderType, psOperand); + bcatcstr(glsl, name); + bcstrfree(name); + } + else + { + bformata(glsl, "Input%d", psOperand->ui32RegisterNumber); + } + } + } + break; + } + } + break; + } + case OPERAND_TYPE_OUTPUT: + { + bformata(glsl, "Output%d", psOperand->ui32RegisterNumber); + if (psOperand->psSubOperand[0]) + { + bcatcstr(glsl, "[int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")]"); + } + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH: + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + { + bcatcstr(glsl, "gl_FragDepth"); + break; + } + case OPERAND_TYPE_TEMP: + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, psOperand); + bcatcstr(glsl, "Temp"); + + if ((psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING) == 0 || psContext->psShader->eShaderType == HULL_SHADER) + { + if (eType == SVT_INT) + { + bcatcstr(glsl, "_int"); + } + else if (eType == SVT_UINT) + { + bcatcstr(glsl, "_uint"); + } + else if (eType == SVT_DOUBLE) + { + bcatcstr(glsl, "_double"); + } + else if (eType == SVT_VOID || + (ui32TOFlag & TO_FLAG_DESTINATION)) + { + if (ui32TOFlag & TO_FLAG_INTEGER) + { + bcatcstr(glsl, "_int"); + } + else + if (ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) + { + bcatcstr(glsl, "_uint"); + } + } + + bformata(glsl, "[%d]", psOperand->ui32RegisterNumber); + } + else + { + if (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) + bformata(glsl, "%d[0]", psOperand->ui32RegisterNumber); + else + bformata(glsl, "%d", psOperand->ui32RegisterNumber); + } + break; + } + case OPERAND_TYPE_SPECIAL_IMMCONSTINT: + { + bformata(glsl, "IntImmConst%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_SPECIAL_IMMCONST: + { + if (psOperand->psSubOperand[0] != NULL) + { + bformata(glsl, "ImmConstArray[%d + ", psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber]); + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_NONE); + bcatcstr(glsl, "]"); + } + else + { + bformata(glsl, "ImmConst%d", psOperand->ui32RegisterNumber); + } + break; + } + case OPERAND_TYPE_SPECIAL_OUTBASECOLOUR: + { + bcatcstr(glsl, "BaseColour"); + break; + } + case OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR: + { + bcatcstr(glsl, "OffsetColour"); + break; + } + case OPERAND_TYPE_SPECIAL_POSITION: + { + bcatcstr(glsl, "gl_Position"); + break; + } + case OPERAND_TYPE_SPECIAL_FOG: + { + bcatcstr(glsl, "Fog"); + break; + } + case OPERAND_TYPE_SPECIAL_POINTSIZE: + { + bcatcstr(glsl, "gl_PointSize"); + break; + } + case OPERAND_TYPE_SPECIAL_ADDRESS: + { + bcatcstr(glsl, "Address"); + break; + } + case OPERAND_TYPE_SPECIAL_LOOPCOUNTER: + { + bcatcstr(glsl, "LoopCounter"); + pui32IgnoreSwizzle[0] = 1; + break; + } + case OPERAND_TYPE_SPECIAL_TEXCOORD: + { + bformata(glsl, "TexCoord%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_CONSTANT_BUFFER: + { + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + bool addParentheses = false; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + pui32IgnoreSwizzle[0] = 1; + } + + if ((psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) != HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) + { + if (psCBuf) + { + //$Globals. + if (psCBuf->Name[0] == '$') + { + ConvertToUniformBufferName(glsl, psContext->psShader, "$Globals"); + } + else + { + ConvertToUniformBufferName(glsl, psContext->psShader, psCBuf->Name); + } + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(glsl, "."); + } + } + else + { + //bformata(glsl, "cb%d", psOperand->aui32ArraySizes[0]); + } + } + + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + //Work out the variable name. Don't apply swizzle to that variable yet. + int32_t rebase = 0; + + if (psCBuf && !psCBuf->blob) + { + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + if (psContext->flags & HLSLCC_FLAG_QUALCOMM_GLES30_DRIVER_WORKAROUND) + { + if (psVarType->Class == SVC_VECTOR || psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) + { + switch (psVarType->Type) + { + case SVT_FLOAT: + case SVT_FLOAT16: + case SVT_FLOAT10: + { + bformata(glsl, "vec%d(", psVarType->Columns); + break; + } + case SVT_UINT: + case SVT_UINT16: + { + bformata(glsl, "uvec%d(", psVarType->Columns); + break; + } + case SVT_INT: + case SVT_INT16: + case SVT_INT12: + { + bformata(glsl, "ivec%d(", psVarType->Columns); + break; + } + default: + { + ASSERT(0); + break; + } + } + addParentheses = true; + } + else if (psVarType->Class == SVC_SCALAR) + { + switch (psVarType->Type) + { + case SVT_FLOAT: + case SVT_FLOAT16: + case SVT_FLOAT10: + { + bformata(glsl, "float("); + break; + } + case SVT_UINT: + case SVT_UINT16: + { + bformata(glsl, "uint("); + break; + } + case SVT_INT: + case SVT_INT16: + case SVT_INT12: + { + bformata(glsl, "int("); + break; + } + default: + { + ASSERT(0); + break; + } + } + addParentheses = true; + } + } + ShaderVarFullName(glsl, psContext->psShader, psVarType); + } + else if (psCBuf) + { + ConvertToUniformBufferName(glsl, psContext->psShader, psCBuf->Name); + bcatcstr(glsl, "_data"); + index = psOperand->aui32ArraySizes[1]; + } + else + // We don't have a semantic for this variable, so try the raw dump appoach. + { + bformata(glsl, "cb%d.data", psOperand->aui32ArraySizes[0]); // + index = psOperand->aui32ArraySizes[1]; + } + + //Dx9 only? + if (psOperand->psSubOperand[0] != NULL) + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, psOperand->psSubOperand[0]); + if (eType != SVT_INT && eType != SVT_UINT) + { + bcatcstr(glsl, "[int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")]"); + } + else + { + bcatcstr(glsl, "["); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + } + } + else + if (index != -1 && psOperand->psSubOperand[1] != NULL) + { + //Array of matrices is treated as array of vec4s + if (index != -1) + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, psOperand->psSubOperand[1]); + if (eType != SVT_INT && eType != SVT_UINT) + { + bcatcstr(glsl, "[int("); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + bformata(glsl, ") + %d]", index); + } + else + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + bformata(glsl, " + %d]", index); + } + } + } + else if (index != -1) + { + bformata(glsl, "[%d]", index); + } + else if (psOperand->psSubOperand[1] != NULL) + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, psOperand->psSubOperand[1]); + if (eType != SVT_INT && eType != SVT_UINT) + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + } + else + { + bcatcstr(glsl, "[int("); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ")]"); + } + } + + if (addParentheses) + bcatcstr(glsl, ")"); + + if (psVarType && psVarType->Class == SVC_VECTOR) + { + switch (rebase) + { + case 4: + { + if (psVarType->Columns == 2) + { + //.x(GLSL) is .y(HLSL). .y(GLSL) is .z(HLSL) + bcatcstr(glsl, ".xxyx"); + } + else if (psVarType->Columns == 3) + { + //.x(GLSL) is .y(HLSL). .y(GLSL) is .z(HLSL) .z(GLSL) is .w(HLSL) + bcatcstr(glsl, ".xxyz"); + } + break; + } + case 8: + { + if (psVarType->Columns == 2) + { + //.x(GLSL) is .z(HLSL). .y(GLSL) is .w(HLSL) + bcatcstr(glsl, ".xxxy"); + } + break; + } + case 0: + default: + { + //No rebase, but extend to vec4. + if (psVarType->Columns == 2) + { + bcatcstr(glsl, ".xyxx"); + } + else if (psVarType->Columns == 3) + { + bcatcstr(glsl, ".xyzx"); + } + break; + } + } + } + + if (psVarType && psVarType->Class == SVC_SCALAR) + { + *pui32IgnoreSwizzle = 1; + } + } + break; + } + case OPERAND_TYPE_RESOURCE: + { + TextureName(*psContext->currentGLSLString, psContext->psShader, psOperand->ui32RegisterNumber, MAX_RESOURCE_BINDINGS, 0); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_SAMPLER: + { + bformata(glsl, "Sampler%d", psOperand->ui32RegisterNumber); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_FUNCTION_BODY: + { + const uint32_t ui32FuncBody = psOperand->ui32RegisterNumber; + const uint32_t ui32FuncTable = psContext->psShader->aui32FuncBodyToFuncTable[ui32FuncBody]; + //const uint32_t ui32FuncPointer = psContext->psShader->aui32FuncTableToFuncPointer[ui32FuncTable]; + const uint32_t ui32ClassType = psContext->psShader->sInfo.aui32TableIDToTypeID[ui32FuncTable]; + const char* ClassTypeName = &psContext->psShader->sInfo.psClassTypes[ui32ClassType].Name[0]; + const uint32_t ui32UniqueClassFuncIndex = psContext->psShader->ui32NextClassFuncName[ui32ClassType]++; + + bformata(glsl, "%s_Func%d", ClassTypeName, ui32UniqueClassFuncIndex); + break; + } + case OPERAND_TYPE_INPUT_FORK_INSTANCE_ID: + { + bcatcstr(glsl, "forkInstanceID"); + *pui32IgnoreSwizzle = 1; + return; + } + case OPERAND_TYPE_IMMEDIATE_CONSTANT_BUFFER: + { + bcatcstr(glsl, "immediateConstBufferF"); + + if (psOperand->psSubOperand[0]) + { + bcatcstr(glsl, "(int("); //Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, "))"); + } + break; + } + case OPERAND_TYPE_INPUT_DOMAIN_POINT: + { + bcatcstr(glsl, "gl_TessCoord"); + break; + } + case OPERAND_TYPE_INPUT_CONTROL_POINT: + { + if (psOperand->aui32ArraySizes[1] == 0) //Input index zero - position. + { + bformata(glsl, "gl_in[%d].gl_Position", psOperand->aui32ArraySizes[0]); + } + else + { + bformata(glsl, "Input%d[%d]", psOperand->aui32ArraySizes[1], psOperand->aui32ArraySizes[0]); + } + break; + } + case OPERAND_TYPE_NULL: + { + // Null register, used to discard results of operations + bcatcstr(glsl, "//null"); + break; + } + case OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID: + { + bcatcstr(glsl, "gl_InvocationID"); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + { + bcatcstr(glsl, "gl_SampleMask[0]"); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_COVERAGE_MASK: + { + bcatcstr(glsl, "gl_SampleMaskIn[0]"); + //Skip swizzle on scalar types. + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID://SV_DispatchThreadID + { + bcatcstr(glsl, "gl_GlobalInvocationID.xyzz"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID://SV_GroupThreadID + { + bcatcstr(glsl, "gl_WorkGroupID.xyzz"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP://SV_GroupID + { + bcatcstr(glsl, "gl_LocalInvocationID.xyzz"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED://SV_GroupIndex + { + bcatcstr(glsl, "gl_LocalInvocationIndex.xyzz"); + break; + } + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + { + UAVName(*psContext->currentGLSLString, psContext->psShader, psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + bformata(glsl, "TGSM%d", psOperand->ui32RegisterNumber); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_PRIMITIVEID: + { + bcatcstr(glsl, "gl_PrimitiveID"); + break; + } + case OPERAND_TYPE_INDEXABLE_TEMP: + { + bformata(glsl, "TempArray%d", psOperand->aui32ArraySizes[0]); + bformata(glsl, "[%d", psOperand->aui32ArraySizes[1]); + + if (psOperand->psSubOperand[1]) + { + bcatcstr(glsl, "+"); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_UNSIGNED_INTEGER); + } + bcatcstr(glsl, "]"); + break; + } + case OPERAND_TYPE_STREAM: + { + bformata(glsl, "%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_INPUT_GS_INSTANCE_ID: + { + bcatcstr(glsl, "gl_InvocationID"); + break; + } + case OPERAND_TYPE_THIS_POINTER: + { + /* + The "this" register is a register that provides up to 4 pieces of information: + X: Which CB holds the instance data + Y: Base element offset of the instance data within the instance CB + Z: Base sampler index + W: Base Texture index + + Can be different for each function call + */ + break; + } + default: + { + ASSERT(0); + break; + } + } +} + +void TranslateVariableName(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle) +{ + bool hasConstructor = false; + bstring glsl = *psContext->currentGLSLString; + + *pui32IgnoreSwizzle = 0; + + if (psOperand->eType != OPERAND_TYPE_IMMEDIATE32 && + psOperand->eType != OPERAND_TYPE_IMMEDIATE64) + { + if (ui32TOFlag != TO_FLAG_NONE && !(ui32TOFlag & (TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY | TO_FLAG_DECLARATION_NAME))) + { + SHADER_VARIABLE_TYPE requestedType = TypeFlagsToSVTType(ui32TOFlag); + const uint32_t swizCount = psOperand->iNumComponents; + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, psOperand); + + if (!AreTypesCompatible(eType, ui32TOFlag)) + { + if (CanDoDirectCast(eType, requestedType)) + { + bformata(glsl, "%s(", GetConstructorForTypeGLSL(psContext, requestedType, swizCount, false)); + } + else + { + // Direct cast not possible, need to do bitcast. + bformata(glsl, "%s(", GetBitcastOp(eType, requestedType)); + } + + hasConstructor = true; + } + } + } + + if (ui32TOFlag & TO_FLAG_COPY) + { + bcatcstr(glsl, "TempCopy"); + if ((psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING) == 0) + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, psOperand); + switch (eType) + { + case SVT_FLOAT: + break; + case SVT_INT: + bcatcstr(glsl, "_int"); + break; + case SVT_UINT: + bcatcstr(glsl, "_uint"); + break; + case SVT_DOUBLE: + bcatcstr(glsl, "_double"); + break; + default: + ASSERT(0); + break; + } + } + } + else + { + TranslateVariableNameByOperandType(psContext, psOperand, ui32TOFlag, pui32IgnoreSwizzle); + } + + if (hasConstructor) + { + bcatcstr(glsl, ")"); + } +} +SHADER_VARIABLE_TYPE GetOperandDataType(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + if (HavePrecisionQualifers(psContext->psShader->eTargetLanguage)) + { + // The min precision qualifier overrides all of the stuff below + switch (psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_FLOAT_16: + return SVT_FLOAT16; + case OPERAND_MIN_PRECISION_FLOAT_2_8: + return SVT_FLOAT10; + case OPERAND_MIN_PRECISION_SINT_16: + return SVT_INT16; + case OPERAND_MIN_PRECISION_UINT_16: + return SVT_UINT16; + default: + break; + } + } + + switch (psOperand->eType) + { + case OPERAND_TYPE_TEMP: + { + SHADER_VARIABLE_TYPE eCurrentType = SVT_VOID; + int i = 0; + + if (psContext->flags & HLSLCC_FLAG_AVOID_TEMP_REGISTER_ALIASING && psContext->psShader->eShaderType != HULL_SHADER) + { + return psContext->psShader->aeCommonTempVecType[psOperand->ui32RegisterNumber]; + } + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + return psOperand->aeDataType[psOperand->aui32Swizzle[0]]; + } + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + return psOperand->aeDataType[0]; + } + + return psOperand->aeDataType[psOperand->aui32Swizzle[0]]; + } + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + for (; i < 4; ++i) + { + if (ui32CompMask & (1 << i)) + { + eCurrentType = psOperand->aeDataType[i]; + break; + } + } + +#ifdef _DEBUG + //Check if all elements have the same basic type. + for (; i < 4; ++i) + { + if (psOperand->ui32CompMask & (1 << i)) + { + if (eCurrentType != psOperand->aeDataType[i]) + { + ASSERT(0); + } + } + } +#endif + return eCurrentType; + } + + ASSERT(0); + + break; + } + case OPERAND_TYPE_OUTPUT: + { + const uint32_t ui32Register = psOperand->aui32ArraySizes[psOperand->iIndexDims - 1]; + InOutSignature* psOut; + + if (GetOutputSignatureFromRegister(ui32Register, psOperand->ui32CompMask, 0, &psContext->psShader->sInfo, &psOut)) + { + if (psOut->eComponentType == INOUT_COMPONENT_UINT32) + { + return SVT_UINT; + } + else if (psOut->eComponentType == INOUT_COMPONENT_SINT32) + { + return SVT_INT; + } + } + break; + } + case OPERAND_TYPE_INPUT: + { + const uint32_t ui32Register = psOperand->aui32ArraySizes[psOperand->iIndexDims - 1]; + InOutSignature* psIn; + + //UINT in DX, INT in GL. + if (psOperand->eSpecialName == NAME_PRIMITIVE_ID) + { + return SVT_INT; + } + + if (GetInputSignatureFromRegister(ui32Register, &psContext->psShader->sInfo, &psIn)) + { + if (psIn->eComponentType == INOUT_COMPONENT_UINT32) + { + return SVT_UINT; + } + else if (psIn->eComponentType == INOUT_COMPONENT_SINT32) + { + return SVT_INT; + } + } + break; + } + case OPERAND_TYPE_CONSTANT_BUFFER: + { + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + int32_t rebase = -1; + int foundVar; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + if (psCBuf && !psCBuf->blob) + { + foundVar = GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + if (foundVar && index == -1 && psOperand->psSubOperand[1] == NULL) + { + return psVarType->Type; + } + } + else + { + // Todo: this isn't correct yet. + return SVT_FLOAT; + } + break; + } + case OPERAND_TYPE_IMMEDIATE32: + { + return psOperand->iIntegerImmediate ? SVT_INT : SVT_FLOAT; + } + + case OPERAND_TYPE_INPUT_THREAD_ID: + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: + { + return SVT_UINT; + } + case OPERAND_TYPE_SPECIAL_ADDRESS: + { + return SVT_INT; + } + default: + { + return SVT_FLOAT; + } + } + + return SVT_FLOAT; +} + +void TranslateOperand(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag) +{ + bstring glsl = *psContext->currentGLSLString; + uint32_t ui32IgnoreSwizzle = 0; + + if (ui32TOFlag & TO_FLAG_NAME_ONLY) + { + TranslateVariableName(psContext, psOperand, ui32TOFlag, &ui32IgnoreSwizzle); + return; + } + + switch (psOperand->eModifier) + { + case OPERAND_MODIFIER_NONE: + { + break; + } + case OPERAND_MODIFIER_NEG: + { + bcatcstr(glsl, "-"); + break; + } + case OPERAND_MODIFIER_ABS: + { + bcatcstr(glsl, "abs("); + break; + } + case OPERAND_MODIFIER_ABSNEG: + { + bcatcstr(glsl, "-abs("); + break; + } + } + + TranslateVariableName(psContext, psOperand, ui32TOFlag, &ui32IgnoreSwizzle); + + if (!ui32IgnoreSwizzle || IsGmemReservedSlot(FBF_ANY, psOperand->ui32RegisterNumber)) + { + TranslateOperandSwizzle(psContext, psOperand); + } + + switch (psOperand->eModifier) + { + case OPERAND_MODIFIER_NONE: + { + break; + } + case OPERAND_MODIFIER_NEG: + { + break; + } + case OPERAND_MODIFIER_ABS: + { + bcatcstr(glsl, ")"); + break; + } + case OPERAND_MODIFIER_ABSNEG: + { + bcatcstr(glsl, ")"); + break; + } + } +} + +char ShaderTypePrefix(Shader* psShader) +{ + switch (psShader->eShaderType) + { + default: + ASSERT(0); + case PIXEL_SHADER: + return 'p'; + case VERTEX_SHADER: + return 'v'; + case GEOMETRY_SHADER: + return 'g'; + case HULL_SHADER: + return 'h'; + case DOMAIN_SHADER: + return 'd'; + case COMPUTE_SHADER: + return 'c'; + } +} + +char ResourceGroupPrefix(ResourceGroup eResGroup) +{ + switch (eResGroup) + { + default: + ASSERT(0); + case RGROUP_CBUFFER: + return 'c'; + case RGROUP_TEXTURE: + return 't'; + case RGROUP_SAMPLER: + return 's'; + case RGROUP_UAV: + return 'u'; + } +} + +void ResourceName(bstring output, Shader* psShader, const char* szName, ResourceGroup eGroup, const char* szSecondaryName, ResourceGroup eSecondaryGroup, uint32_t ui32ArrayOffset, const char* szModifier) +{ + + const char* pBracket; + + bconchar(output, ShaderTypePrefix(psShader)); + bcatcstr(output, szModifier); + + bconchar(output, ResourceGroupPrefix(eGroup)); + while ((pBracket = strpbrk(szName, "[]")) != NULL) + { + //array syntax [X] becomes _0_ + //Otherwise declarations could end up as: + //uniform sampler2D SomeTextures[0]; + //uniform sampler2D SomeTextures[1]; + bcatblk(output, (const void*)szName, (int)(pBracket - szName)); + bconchar(output, '_'); + szName = pBracket + 1; + } + bcatcstr(output, szName); + + if (ui32ArrayOffset) + { + bformata(output, "%d", ui32ArrayOffset); + } + + if (szSecondaryName != NULL) + { + bconchar(output, ResourceGroupPrefix(eSecondaryGroup)); + bcatcstr(output, szSecondaryName); + } +} + +void TextureName(bstring output, Shader* psShader, const uint32_t ui32TextureRegister, const uint32_t ui32SamplerRegister, const int bCompare) +{ + ResourceBinding* psTextureBinding = 0; + ResourceBinding* psSamplerBinding = 0; + int found; + const char* szModifier = bCompare ? "c" : ""; + + found = GetResourceFromBindingPoint(RGROUP_TEXTURE, ui32TextureRegister, &psShader->sInfo, &psTextureBinding); + if (ui32SamplerRegister < MAX_RESOURCE_BINDINGS) + { + found &= GetResourceFromBindingPoint(RGROUP_SAMPLER, ui32SamplerRegister, &psShader->sInfo, &psSamplerBinding); + } + + if (found) + { + if (IsGmemReservedSlot(FBF_EXT_COLOR, ui32TextureRegister) || IsGmemReservedSlot(FBF_ARM_COLOR, ui32TextureRegister)) // FRAMEBUFFER FETCH + { + int regNum = GetGmemInputResourceSlot(ui32TextureRegister); + bformata(output, "GMEM_Input%d", regNum); + } + else if (IsGmemReservedSlot(FBF_ARM_DEPTH, ui32TextureRegister)) + { + bcatcstr(output, "GMEM_Depth"); + } + else if (IsGmemReservedSlot(FBF_ARM_STENCIL, ui32TextureRegister)) + { + bcatcstr(output, "GMEM_Stencil"); + } + else + { + ResourceName(output, psShader, psTextureBinding->Name, RGROUP_TEXTURE, psSamplerBinding ? psSamplerBinding->Name : NULL, RGROUP_SAMPLER, ui32TextureRegister - psTextureBinding->ui32BindPoint, szModifier); + } + } + else if (ui32SamplerRegister < MAX_RESOURCE_BINDINGS) + { + bformata(output, "UnknownTexture%s_%d_%d", szModifier, ui32TextureRegister, ui32SamplerRegister); + } + else + { + bformata(output, "UnknownTexture%s_%d", szModifier, ui32TextureRegister); + } +} + +void UAVName(bstring output, Shader* psShader, const uint32_t ui32RegisterNumber) +{ + ResourceBinding* psBinding = 0; + int found; + + found = GetResourceFromBindingPoint(RGROUP_UAV, ui32RegisterNumber, &psShader->sInfo, &psBinding); + + if (found) + { + ResourceName(output, psShader, psBinding->Name, RGROUP_UAV, NULL, RGROUP_COUNT, ui32RegisterNumber - psBinding->ui32BindPoint, ""); + } + else + { + bformata(output, "UnknownUAV%d", ui32RegisterNumber); + } +} + +void UniformBufferName(bstring output, Shader* psShader, const uint32_t ui32RegisterNumber) +{ + ResourceBinding* psBinding = 0; + int found; + + found = GetResourceFromBindingPoint(RGROUP_CBUFFER, ui32RegisterNumber, &psShader->sInfo, &psBinding); + + if (found) + { + ResourceName(output, psShader, psBinding->Name, RGROUP_CBUFFER, NULL, RGROUP_COUNT, ui32RegisterNumber - psBinding->ui32BindPoint, ""); + } + else + { + bformata(output, "UnknownUniformBuffer%d", ui32RegisterNumber); + } +} + +void ShaderVarName(bstring output, Shader* psShader, const char* OriginalName) +{ + bconchar(output, ShaderTypePrefix(psShader)); + bcatcstr(output, OriginalName); +} + +void ShaderVarFullName(bstring output, Shader* psShader, const ShaderVarType* psShaderVar) +{ + if (psShaderVar->Parent != NULL) + { + ShaderVarFullName(output, psShader, psShaderVar->Parent); + bconchar(output, '.'); + } + ShaderVarName(output, psShader, psShaderVar->Name); +} + +void ConvertToTextureName(bstring output, Shader* psShader, const char* szName, const char* szSamplerName, const int bCompare) +{ + (void)bCompare; + + ResourceName(output, psShader, szName, RGROUP_TEXTURE, szSamplerName, RGROUP_SAMPLER, 0, ""); +} + +void ConvertToUAVName(bstring output, Shader* psShader, const char* szOriginalUAVName) +{ + ResourceName(output, psShader, szOriginalUAVName, RGROUP_UAV, NULL, RGROUP_COUNT, 0, ""); +} + +void ConvertToUniformBufferName(bstring output, Shader* psShader, const char* szConstantBufferName) +{ + ResourceName(output, psShader, szConstantBufferName, RGROUP_CBUFFER, NULL, RGROUP_COUNT, 0, ""); +} + +uint32_t GetGmemInputResourceSlot(uint32_t const slotIn) +{ + if (slotIn == GMEM_ARM_COLOR_SLOT) + { + // ARM framebuffer fetch only works with COLOR0 + return 0; + } + if (slotIn >= GMEM_FLOAT4_START_SLOT) + { + return slotIn - GMEM_FLOAT4_START_SLOT; + } + if (slotIn >= GMEM_FLOAT3_START_SLOT) + { + return slotIn - GMEM_FLOAT3_START_SLOT; + } + if (slotIn >= GMEM_FLOAT2_START_SLOT) + { + return slotIn - GMEM_FLOAT2_START_SLOT; + } + if (slotIn >= GMEM_FLOAT_START_SLOT) + { + return slotIn - GMEM_FLOAT_START_SLOT; + } + return slotIn; +} + +uint32_t GetGmemInputResourceNumElements(uint32_t const slotIn) +{ + if (slotIn >= GMEM_FLOAT4_START_SLOT) + { + return 4; + } + if (slotIn >= GMEM_FLOAT3_START_SLOT) + { + return 3; + } + if (slotIn >= GMEM_FLOAT2_START_SLOT) + { + return 2; + } + if (slotIn >= GMEM_FLOAT_START_SLOT) + { + return 1; + } + return 0; +} + +void TranslateGmemOperandSwizzleWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask, uint32_t gmemNumElements) +{ + // Similar as TranslateOperandSwizzleWithMaskMETAL but need to considerate max # of elements + + bstring metal = *psContext->currentGLSLString; + + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents != 1) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t mask; + if (psOperand->ui32CompMask != 0) + { + mask = psOperand->ui32CompMask & ui32ComponentMask; + } + else + { + mask = ui32ComponentMask; + } + + if (mask != 0 && mask != OPERAND_4_COMPONENT_MASK_ALL) + { + bcatcstr(metal, "."); + if (mask & OPERAND_4_COMPONENT_MASK_X) + { + bcatcstr(metal, "x"); + } + if (mask & OPERAND_4_COMPONENT_MASK_Y) + { + if (gmemNumElements < 2) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "y"); + } + } + if (mask & OPERAND_4_COMPONENT_MASK_Z) + { + if (gmemNumElements < 3) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "z"); + } + } + if (mask & OPERAND_4_COMPONENT_MASK_W) + { + if (gmemNumElements < 4) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "w"); + } + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (ui32ComponentMask != OPERAND_4_COMPONENT_MASK_ALL || + !(psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X && + psOperand->aui32Swizzle[1] == OPERAND_4_COMPONENT_Y && + psOperand->aui32Swizzle[2] == OPERAND_4_COMPONENT_Z && + psOperand->aui32Swizzle[3] == OPERAND_4_COMPONENT_W + ) + ) + { + uint32_t i; + + bcatcstr(metal, "."); + + for (i = 0; i < 4; ++i) + { + if (!(ui32ComponentMask & (OPERAND_4_COMPONENT_MASK_X << i))) + { + continue; + } + + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + bcatcstr(metal, "x"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + if (gmemNumElements < 2) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "y"); + } + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + if (gmemNumElements < 3) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "z"); + } + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + if (gmemNumElements < 4) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "w"); + } + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) // ui32ComponentMask is ignored in this case + { + bcatcstr(metal, "."); + + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + bcatcstr(metal, "x"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + if (gmemNumElements < 2) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "y"); + } + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + if (gmemNumElements < 3) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "z"); + } + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + if (gmemNumElements < 4) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "w"); + } + } + } + + //Component Select 1 + } +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/CMakeLists.txt b/Code/Tools/HLSLCrossCompilerMETAL/CMakeLists.txt new file mode 100644 index 0000000000..33fcc54e34 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/CMakeLists.txt @@ -0,0 +1,54 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +if (PAL_TRAIT_BUILD_HOST_TOOLS) + + ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}) + + include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + if (NOT PAL_TRAIT_BUILD_HLSLCC_METAL) + return() + endif() + + ly_add_target( + NAME HLSLcc_Metal EXECUTABLE + NAMESPACE AZ + OUTPUT_NAME HLSLcc + OUTPUT_SUBDIRECTORY Compiler/PCGMETAL/HLSLcc + FILES_CMAKE + hlslcc_metal_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + include + src + src/cbstring + offline/cjson + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + ) + ly_add_source_properties( + SOURCES + offline/compilerStandalone.cpp + offline/cjson/cJSON.c + src/toGLSL.c + src/toGLSLDeclaration.c + src/cbstring/bstrlib.c + src/cbstring/bstraux.c + src/reflect.c + src/decode.c + src/toMETAL.c + src/toMETALDeclaration.c + PROPERTY COMPILE_DEFINITIONS + VALUES _CRT_SECURE_NO_WARNINGS + ) + +endif() diff --git a/Code/Tools/HLSLCrossCompilerMETAL/Platform/Linux/PAL_linux.cmake b/Code/Tools/HLSLCrossCompilerMETAL/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..6dc23ee057 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(PAL_TRAIT_BUILD_HLSLCC_METAL FALSE) \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompilerMETAL/Platform/Mac/PAL_mac.cmake b/Code/Tools/HLSLCrossCompilerMETAL/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..6dc23ee057 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(PAL_TRAIT_BUILD_HLSLCC_METAL FALSE) \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompilerMETAL/Platform/Windows/PAL_windows.cmake b/Code/Tools/HLSLCrossCompilerMETAL/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..ee003b245b --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(PAL_TRAIT_BUILD_HLSLCC_METAL TRUE) \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompilerMETAL/README b/Code/Tools/HLSLCrossCompilerMETAL/README new file mode 100644 index 0000000000..2f36e0966e --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/README @@ -0,0 +1,52 @@ +What does this software do? + Cross compiles HLSL bytecode to GLSL or GLSL ES. It also provides functions to + decode the reflection information embedded in HLSL bytecode. Both offline and online compiliation + is supported. + +Supported bytecode formats: + cs_4_0 cs_4_1 cs_5_0 + ds_5_0 + hs_5_0 + gs_4_0 gs_4_1 gs_5_0 + ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3 ps_4_0_level_9_0 ps_4_1 ps_5_0 + vs_4_0_level_9_3 vs_4_0_level_9_0 vs_4_1 vs_5_0 + +Work is underway to support the DX9 bytecode formats: + ps_2_0 ps_2_a ps_2_b ps_3_0 + vs_1_1 vs_2_0 vs_2_a vs_3_0 + +Supported target languages: + GLSL ES 100 + GLSL ES 300 + GLSL ES 310 + GLSL 120 + GLSL 130 + GLSL 140 + GLSL 150 + GLSL 330 + GLSL 400 + GLSL 410 + GLSL 420 + GLSL 430 + GLSL 440 + METAL + +I have plans to add support for more target languages including: + ARB assembly (ARB_vertex_program et al.) + NVIDIA assembly (NV_vertex_program et al.) + +If the source shader contains instructions not support by the target language then compilation is allowed +to fail at the GLSL compile stage, i.e. the cross compiler may not generate errors/warnings but an OpenGL +driver will reject the shader. + +The tests directory contains HLSL, bytecode and asm versions of some shaders used to verify this decoder. +There are also a few sample applications used to make sure that generated GLSL is correct. + +A cmake makefile can be found in the mk directory. + +Generating hlsl_opcode_funcs_glsl.h + Use fwrap.py -f hlsl_opcode_funcs.glsl + fwrap.py can be found in my Helpful-scripts github repository. + +For further information please see the Wiki page for this project at +https://github.com/James-Jones/HLSLCrossCompiler/wiki. diff --git a/Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc.exe b/Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc.exe new file mode 100644 index 0000000000..f1206847af --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35285dbf53617bf58f22035bb502d0b3328678344245635de578c2e73d484d04 +size 216064 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc_d.exe b/Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc_d.exe new file mode 100644 index 0000000000..64dab82ae4 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/bin/win32/HLSLcc_d.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99f686d3fc04c80f3460e6d35507acb09f5975f7c4d5e5cae95834e48a46898a +size 462848 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/hlslcc_metal_files.cmake b/Code/Tools/HLSLCrossCompilerMETAL/hlslcc_metal_files.cmake new file mode 100644 index 0000000000..ffeb9d9755 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/hlslcc_metal_files.cmake @@ -0,0 +1,65 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + include/hlslcc.h + include/hlslcc.hpp + include/pstdint.h + include/hlslcc_bin.hpp + offline/hash.h + offline/serializeReflection.h + offline/timer.h + offline/compilerStandalone.cpp + offline/serializeReflection.cpp + offline/timer.cpp + offline/cjson/cJSON.h + offline/cjson/cJSON.c + src/decode.c + src/decodeDX9.c + src/reflect.c + src/toGLSL.c + src/toMETAL.c + src/toMETALDeclaration.c + src/toMETALInstruction.c + src/toMETALOperand.c + src/toGLSLDeclaration.c + src/toGLSLInstruction.c + src/toGLSLOperand.c + src/internal_includes/debug.h + src/internal_includes/decode.h + src/internal_includes/hlslcc_malloc.h + src/internal_includes/hlslcc_malloc.c + src/internal_includes/languages.h + src/internal_includes/reflect.h + src/internal_includes/shaderLimits.h + src/internal_includes/structs.h + src/internal_includes/toMETALDeclaration.h + src/internal_includes/toMETALInstruction.h + src/internal_includes/toMETALOperand.h + src/internal_includes/toGLSLDeclaration.h + src/internal_includes/toGLSLInstruction.h + src/internal_includes/toGLSLOperand.h + src/internal_includes/tokens.h + src/internal_includes/tokensDX9.h + src/internal_includes/structsMetal.h + src/internal_includes/structsMetal.c + src/cbstring/bsafe.h + src/cbstring/bstraux.h + src/cbstring/bstrlib.h + src/cbstring/bsafe.c + src/cbstring/bstraux.c + src/cbstring/bstrlib.c +) + +set(SKIP_UNITY_BUILD_INCLUSION_FILES + # 'bsafe.c' tries to forward declar 'strncpy', 'strncat', etc, but they are already declared in other modules. Remove from unity builds conideration + src/cbstring/bsafe.c +) \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.h b/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.h new file mode 100644 index 0000000000..b7444121bc --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.h @@ -0,0 +1,537 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef HLSLCC_H_ +#define HLSLCC_H_ + +#if defined (_WIN32) && defined(HLSLCC_DYNLIB) + #define HLSLCC_APIENTRY __stdcall + #if defined(libHLSLcc_EXPORTS) + #define HLSLCC_API __declspec(dllexport) + #else + #define HLSLCC_API __declspec(dllimport) + #endif +#else + #define HLSLCC_APIENTRY + #define HLSLCC_API +#endif + +#include +#include + +#ifndef __cplusplus + #ifndef max + #define max(a,b) (((a) > (b)) ? (a) : (b)) + #endif + + #ifndef min + #define min(a,b) (((a) < (b)) ? (a) : (b)) + #endif +#endif //__cplusplus + +typedef enum +{ + LANG_DEFAULT,// Depends on the HLSL shader model. + LANG_ES_100, + LANG_ES_300, + LANG_ES_310, + LANG_120, + LANG_130, + LANG_140, + LANG_150, + LANG_330, + LANG_400, + LANG_410, + LANG_420, + LANG_430, + LANG_440, + // CONFETTI + LANG_METAL, +} ShaderLang; + +typedef struct +{ + uint32_t ARB_explicit_attrib_location : 1; + uint32_t ARB_explicit_uniform_location : 1; + uint32_t ARB_shading_language_420pack : 1; +}GlExtensions; + +enum +{ + MAX_SHADER_VEC4_OUTPUT = 512 +}; +enum +{ + MAX_SHADER_VEC4_INPUT = 512 +}; +enum +{ + MAX_TEXTURES = 128 +}; +enum +{ + MAX_FORK_PHASES = 2 +}; +enum +{ + MAX_FUNCTION_BODIES = 1024 +}; +enum +{ + MAX_CLASS_TYPES = 1024 +}; +enum +{ + MAX_FUNCTION_POINTERS = 128 +}; + +//Reflection +#define MAX_REFLECT_STRING_LENGTH 512 +#define MAX_CBUFFERS 256 +#define MAX_UAV 256 +#define MAX_FUNCTION_TABLES 256 +#define MAX_RESOURCE_BINDINGS 256 + +typedef enum SPECIAL_NAME +{ + NAME_UNDEFINED = 0, + NAME_POSITION = 1, + NAME_CLIP_DISTANCE = 2, + NAME_CULL_DISTANCE = 3, + NAME_RENDER_TARGET_ARRAY_INDEX = 4, + NAME_VIEWPORT_ARRAY_INDEX = 5, + NAME_VERTEX_ID = 6, + NAME_PRIMITIVE_ID = 7, + NAME_INSTANCE_ID = 8, + NAME_IS_FRONT_FACE = 9, + NAME_SAMPLE_INDEX = 10, + // The following are added for D3D11 + NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR = 11, + NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR = 12, + NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR = 13, + NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR = 14, + NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR = 15, + NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR = 16, + NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR = 17, + NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR = 18, + NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR = 19, + NAME_FINAL_TRI_INSIDE_TESSFACTOR = 20, + NAME_FINAL_LINE_DETAIL_TESSFACTOR = 21, + NAME_FINAL_LINE_DENSITY_TESSFACTOR = 22, +} SPECIAL_NAME; + + +typedef enum +{ + INOUT_COMPONENT_UNKNOWN = 0, + INOUT_COMPONENT_UINT32 = 1, + INOUT_COMPONENT_SINT32 = 2, + INOUT_COMPONENT_FLOAT32 = 3 +} INOUT_COMPONENT_TYPE; + +typedef enum MIN_PRECISION +{ + MIN_PRECISION_DEFAULT = 0, + MIN_PRECISION_FLOAT_16 = 1, + MIN_PRECISION_FLOAT_2_8 = 2, + MIN_PRECISION_RESERVED = 3, + MIN_PRECISION_SINT_16 = 4, + MIN_PRECISION_UINT_16 = 5, + MIN_PRECISION_ANY_16 = 0xf0, + MIN_PRECISION_ANY_10 = 0xf1 +} MIN_PRECISION; + +typedef struct InOutSignature_TAG +{ + char SemanticName[MAX_REFLECT_STRING_LENGTH]; + uint32_t ui32SemanticIndex; + SPECIAL_NAME eSystemValueType; + INOUT_COMPONENT_TYPE eComponentType; + uint32_t ui32Register; + uint32_t ui32Mask; + uint32_t ui32ReadWriteMask; + + uint32_t ui32Stream; + MIN_PRECISION eMinPrec; +} InOutSignature; + +typedef enum ResourceType_TAG +{ + RTYPE_CBUFFER,//0 + RTYPE_TBUFFER,//1 + RTYPE_TEXTURE,//2 + RTYPE_SAMPLER,//3 + RTYPE_UAV_RWTYPED,//4 + RTYPE_STRUCTURED,//5 + RTYPE_UAV_RWSTRUCTURED,//6 + RTYPE_BYTEADDRESS,//7 + RTYPE_UAV_RWBYTEADDRESS,//8 + RTYPE_UAV_APPEND_STRUCTURED,//9 + RTYPE_UAV_CONSUME_STRUCTURED,//10 + RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER,//11 + RTYPE_COUNT, +} ResourceType; + +typedef enum ResourceGroup_TAG +{ + RGROUP_CBUFFER, + RGROUP_TEXTURE, + RGROUP_SAMPLER, + RGROUP_UAV, + RGROUP_COUNT, +} ResourceGroup; + +typedef enum UAVBindingArea_TAG +{ + UAVAREA_INVALID, + UAVAREA_CBUFFER, + UAVAREA_TEXTURE, + UAVAREA_COUNT, +} UAVBindingArea; + +typedef enum REFLECT_RESOURCE_DIMENSION +{ + REFLECT_RESOURCE_DIMENSION_UNKNOWN = 0, + REFLECT_RESOURCE_DIMENSION_BUFFER = 1, + REFLECT_RESOURCE_DIMENSION_TEXTURE1D = 2, + REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY = 3, + REFLECT_RESOURCE_DIMENSION_TEXTURE2D = 4, + REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY = 5, + REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS = 6, + REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY = 7, + REFLECT_RESOURCE_DIMENSION_TEXTURE3D = 8, + REFLECT_RESOURCE_DIMENSION_TEXTURECUBE = 9, + REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY = 10, + REFLECT_RESOURCE_DIMENSION_BUFFEREX = 11, +} REFLECT_RESOURCE_DIMENSION; + +typedef struct ResourceBinding_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + ResourceType eType; + uint32_t ui32BindPoint; + uint32_t ui32BindCount; + uint32_t ui32Flags; + REFLECT_RESOURCE_DIMENSION eDimension; + uint32_t ui32ReturnType; + uint32_t ui32NumSamples; + UAVBindingArea eBindArea; +} ResourceBinding; + +typedef enum _SHADER_VARIABLE_TYPE +{ + SVT_VOID = 0, + SVT_BOOL = 1, + SVT_INT = 2, + SVT_FLOAT = 3, + SVT_STRING = 4, + SVT_TEXTURE = 5, + SVT_TEXTURE1D = 6, + SVT_TEXTURE2D = 7, + SVT_TEXTURE3D = 8, + SVT_TEXTURECUBE = 9, + SVT_SAMPLER = 10, + SVT_PIXELSHADER = 15, + SVT_VERTEXSHADER = 16, + SVT_UINT = 19, + SVT_UINT8 = 20, + SVT_GEOMETRYSHADER = 21, + SVT_RASTERIZER = 22, + SVT_DEPTHSTENCIL = 23, + SVT_BLEND = 24, + SVT_BUFFER = 25, + SVT_CBUFFER = 26, + SVT_TBUFFER = 27, + SVT_TEXTURE1DARRAY = 28, + SVT_TEXTURE2DARRAY = 29, + SVT_RENDERTARGETVIEW = 30, + SVT_DEPTHSTENCILVIEW = 31, + SVT_TEXTURE2DMS = 32, + SVT_TEXTURE2DMSARRAY = 33, + SVT_TEXTURECUBEARRAY = 34, + SVT_HULLSHADER = 35, + SVT_DOMAINSHADER = 36, + SVT_INTERFACE_POINTER = 37, + SVT_COMPUTESHADER = 38, + SVT_DOUBLE = 39, + SVT_RWTEXTURE1D = 40, + SVT_RWTEXTURE1DARRAY = 41, + SVT_RWTEXTURE2D = 42, + SVT_RWTEXTURE2DARRAY = 43, + SVT_RWTEXTURE3D = 44, + SVT_RWBUFFER = 45, + SVT_BYTEADDRESS_BUFFER = 46, + SVT_RWBYTEADDRESS_BUFFER = 47, + SVT_STRUCTURED_BUFFER = 48, + SVT_RWSTRUCTURED_BUFFER = 49, + SVT_APPEND_STRUCTURED_BUFFER = 50, + SVT_CONSUME_STRUCTURED_BUFFER = 51, + + // Partial precision types + SVT_FLOAT10 = 53, + SVT_FLOAT16 = 54, + + + SVT_FORCE_DWORD = 0x7fffffff +} SHADER_VARIABLE_TYPE; + +typedef enum _SHADER_VARIABLE_CLASS +{ + SVC_SCALAR = 0, + SVC_VECTOR = (SVC_SCALAR + 1), + SVC_MATRIX_ROWS = (SVC_VECTOR + 1), + SVC_MATRIX_COLUMNS = (SVC_MATRIX_ROWS + 1), + SVC_OBJECT = (SVC_MATRIX_COLUMNS + 1), + SVC_STRUCT = (SVC_OBJECT + 1), + SVC_INTERFACE_CLASS = (SVC_STRUCT + 1), + SVC_INTERFACE_POINTER = (SVC_INTERFACE_CLASS + 1), + SVC_FORCE_DWORD = 0x7fffffff +} SHADER_VARIABLE_CLASS; + +typedef struct ShaderVarType_TAG +{ + SHADER_VARIABLE_CLASS Class; + SHADER_VARIABLE_TYPE Type; + uint32_t Rows; + uint32_t Columns; + uint32_t Elements; + uint32_t MemberCount; + uint32_t Offset; + char Name[MAX_REFLECT_STRING_LENGTH]; + + uint32_t ParentCount; + struct ShaderVarType_TAG* Parent; + //Includes all parent names. + char FullName[MAX_REFLECT_STRING_LENGTH]; + + struct ShaderVarType_TAG* Members; +} ShaderVarType; + +typedef struct ShaderVar_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + int haveDefaultValue; + uint32_t* pui32DefaultValues; + //Offset/Size in bytes. + uint32_t ui32StartOffset; + uint32_t ui32Size; + + ShaderVarType sType; +} ShaderVar; + +typedef struct ConstantBuffer_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + + uint32_t ui32NumVars; + ShaderVar* asVars; + + uint32_t ui32TotalSizeInBytes; + int blob; // Used with dynamic indexed const. buffers +} ConstantBuffer; + +typedef struct ClassType_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + uint16_t ui16ID; + uint16_t ui16ConstBufStride; + uint16_t ui16Texture; + uint16_t ui16Sampler; +} ClassType; + +typedef struct ClassInstance_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; + uint16_t ui16ID; + uint16_t ui16ConstBuf; + uint16_t ui16ConstBufOffset; + uint16_t ui16Texture; + uint16_t ui16Sampler; +} ClassInstance; + +typedef enum TESSELLATOR_PARTITIONING +{ + TESSELLATOR_PARTITIONING_UNDEFINED = 0, + TESSELLATOR_PARTITIONING_INTEGER = 1, + TESSELLATOR_PARTITIONING_POW2 = 2, + TESSELLATOR_PARTITIONING_FRACTIONAL_ODD = 3, + TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN = 4 +} TESSELLATOR_PARTITIONING; + +typedef enum TESSELLATOR_OUTPUT_PRIMITIVE +{ + TESSELLATOR_OUTPUT_UNDEFINED = 0, + TESSELLATOR_OUTPUT_POINT = 1, + TESSELLATOR_OUTPUT_LINE = 2, + TESSELLATOR_OUTPUT_TRIANGLE_CW = 3, + TESSELLATOR_OUTPUT_TRIANGLE_CCW = 4 +} TESSELLATOR_OUTPUT_PRIMITIVE; + +typedef struct TextureSamplerPair_TAG +{ + char Name[MAX_REFLECT_STRING_LENGTH]; +} TextureSamplerPair; + +typedef struct TextureSamplerInfo_TAG +{ + uint32_t ui32NumTextureSamplerPairs; + TextureSamplerPair aTextureSamplerPair[MAX_RESOURCE_BINDINGS]; +} TextureSamplerInfo; + +typedef struct ShaderInfo_TAG +{ + uint32_t ui32MajorVersion; + uint32_t ui32MinorVersion; + + uint32_t ui32NumInputSignatures; + InOutSignature* psInputSignatures; + + uint32_t ui32NumOutputSignatures; + InOutSignature* psOutputSignatures; + + uint32_t ui32NumPatchConstantSignatures; + InOutSignature* psPatchConstantSignatures; + + uint32_t ui32NumResourceBindings; + ResourceBinding* psResourceBindings; + + uint32_t ui32NumConstantBuffers; + ConstantBuffer* psConstantBuffers; + ConstantBuffer* psThisPointerConstBuffer; + + uint32_t ui32NumClassTypes; + ClassType* psClassTypes; + + uint32_t ui32NumClassInstances; + ClassInstance* psClassInstances; + + //Func table ID to class name ID. + uint32_t aui32TableIDToTypeID[MAX_FUNCTION_TABLES]; + + uint32_t aui32ResourceMap[RGROUP_COUNT][MAX_RESOURCE_BINDINGS]; + + // Texture index to sampler slot + uint32_t aui32SamplerMap[MAX_RESOURCE_BINDINGS]; + + TESSELLATOR_PARTITIONING eTessPartitioning; + TESSELLATOR_OUTPUT_PRIMITIVE eTessOutPrim; + + //compute shader thread number + uint32_t ui32Thread_x; + uint32_t ui32Thread_y; + uint32_t ui32Thread_z; +} ShaderInfo; + +typedef enum INTERPOLATION_MODE +{ + INTERPOLATION_UNDEFINED = 0, + INTERPOLATION_CONSTANT = 1, + INTERPOLATION_LINEAR = 2, + INTERPOLATION_LINEAR_CENTROID = 3, + INTERPOLATION_LINEAR_NOPERSPECTIVE = 4, + INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID = 5, + INTERPOLATION_LINEAR_SAMPLE = 6, + INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE = 7, +} INTERPOLATION_MODE; + +typedef struct +{ + int shaderType; //One of the GL enums. + char* sourceCode; + ShaderInfo reflection; + ShaderLang GLSLLanguage; + TextureSamplerInfo textureSamplerInfo; // HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS fills this out +} Shader; + +// NOTE: HLSLCC flags are specified by command line when executing this cross compiler. +// If these flags change, the command line switch '-flags=XXX' must change as well. +// Open 3D Engine composes the command line in file 'dev\Code\CryEngine\RenderDll\Common\Shaders\RemoteCompiler.cpp' + +/*HLSL constant buffers are treated as default-block unform arrays by default. This is done + to support versions of GLSL which lack ARB_uniform_buffer_object functionality. + Setting this flag causes each one to have its own uniform block. + Note: Currently the nth const buffer will be named UnformBufferN. This is likey to change to the original HLSL name in the future.*/ +static const unsigned int HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT = 0x1; + +static const unsigned int HLSLCC_FLAG_ORIGIN_UPPER_LEFT = 0x2; + +static const unsigned int HLSLCC_FLAG_PIXEL_CENTER_INTEGER = 0x4; + +static const unsigned int HLSLCC_FLAG_GLOBAL_CONSTS_NEVER_IN_UBO = 0x8; + +//GS enabled? +//Affects vertex shader (i.e. need to compile vertex shader again to use with/without GS). +//This flag is needed in order for the interfaces between stages to match when GS is in use. +//PS inputs VtxGeoOutput +//GS outputs VtxGeoOutput +//Vs outputs VtxOutput if GS enabled. VtxGeoOutput otherwise. +static const unsigned int HLSLCC_FLAG_GS_ENABLED = 0x10; + +static const unsigned int HLSLCC_FLAG_TESS_ENABLED = 0x20; + +//Either use this flag or glBindFragDataLocationIndexed. +//When set the first pixel shader output is the first input to blend +//equation, the others go to the second input. +static const unsigned int HLSLCC_FLAG_DUAL_SOURCE_BLENDING = 0x40; + +//If set, shader inputs and outputs are declared with their semantic name. +static const unsigned int HLSLCC_FLAG_INOUT_SEMANTIC_NAMES = 0x80; +//If set, shader inputs and outputs are declared with their semantic name appended. +static const unsigned int HLSLCC_FLAG_INOUT_APPEND_SEMANTIC_NAMES = 0x100; + +//If set, combines texture/sampler pairs used together into samplers named "texturename_X_samplername". +static const unsigned int HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS = 0x200; + +//If set, attribute and uniform explicit location qualifiers are disabled (even if the language version supports that) +static const unsigned int HLSLCC_FLAG_DISABLE_EXPLICIT_LOCATIONS = 0x400; + +//If set, global uniforms are not stored in a struct. +static const unsigned int HLSLCC_FLAG_DISABLE_GLOBALS_STRUCT = 0x800; + +// If set, HLSL DX9 lower precision qualifiers (e.g half) will be transformed to DX11 style (e.g min16float) +// before compiling. Necessary to preserve precision information. If not, FXC just silently transform +// everything to full precision (e.g float32). +static const unsigned int HLSLCC_FLAG_HALF_FLOAT_TRANSFORM = 0x40000; + +#ifdef __cplusplus +extern "C" { +#endif + +HLSLCC_API void HLSLCC_APIENTRY HLSLcc_SetMemoryFunctions(void* (*malloc_override)(size_t), + void* (*calloc_override)(size_t, size_t), + void (* free_override)(void*), + void* (*realloc_override)(void*, size_t)); + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromFileToGLSL(const char* filename, + unsigned int flags, + ShaderLang language, + const GlExtensions* extensions, + Shader* result + ); + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromMemToGLSL(const char* shader, + unsigned int flags, + ShaderLang language, + const GlExtensions* extensions, + Shader* result); + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromFileToMETAL(const char* filename, + unsigned int flags, + ShaderLang language, + Shader* result + ); + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromMemToMETAL(const char* shader, + unsigned int flags, + ShaderLang language, + Shader* result); + + +HLSLCC_API void HLSLCC_APIENTRY FreeShader(Shader*); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.hpp b/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.hpp new file mode 100644 index 0000000000..193415f277 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc.hpp @@ -0,0 +1,7 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +extern "C" { +#include "hlslcc.h" +} + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc_bin.hpp b/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc_bin.hpp new file mode 100644 index 0000000000..cc41572aa2 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/include/hlslcc_bin.hpp @@ -0,0 +1,448 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#define FOURCC(a, b, c, d) ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24)) + +enum +{ + DXBC_BASE_ALIGNMENT = 4, + FOURCC_DXBC = FOURCC('D', 'X', 'B', 'C'), + FOURCC_RDEF = FOURCC('R', 'D', 'E', 'F'), + FOURCC_ISGN = FOURCC('I', 'S', 'G', 'N'), + FOURCC_OSGN = FOURCC('O', 'S', 'G', 'N'), + FOURCC_PCSG = FOURCC('P', 'C', 'S', 'G'), + FOURCC_SHDR = FOURCC('S', 'H', 'D', 'R'), + FOURCC_SHEX = FOURCC('S', 'H', 'E', 'X'), + FOURCC_GLSL = FOURCC('G', 'L', 'S', 'L'), + FOURCC_ISG1 = FOURCC('I', 'S', 'G', '1'), // When lower precision float/int/uint is used + FOURCC_OSG1 = FOURCC('O', 'S', 'G', '1'), // When lower precision float/int/uint is used +}; + +#undef FOURCC + +template +inline T DXBCSwapBytes(const T& kValue) +{ + return kValue; +} + +#if defined(__BIG_ENDIAN__) || SYSTEM_IS_BIG_ENDIAN + +inline uint16_t DXBCSwapBytes(const uint16_t& uValue) +{ + return + (((uValue) >> 8) & 0xFF) | + (((uValue) << 8) & 0xFF); +} + +inline uint32_t DXBCSwapBytes(const uint32_t& uValue) +{ + return + (((uValue) >> 24) & 0x000000FF) | + (((uValue) >> 8) & 0x0000FF00) | + (((uValue) << 8) & 0x00FF0000) | + (((uValue) << 24) & 0xFF000000); +} + +#endif //defined(__BIG_ENDIAN__) || SYSTEM_IS_BIG_ENDIAN + +template +struct SDXBCBufferBase +{ + Element* m_pBegin; + Element* m_pEnd; + Element* m_pIter; + + SDXBCBufferBase(Element* pBegin, Element* pEnd) + : m_pBegin(pBegin) + , m_pEnd(pEnd) + , m_pIter(pBegin) + { + } + + bool SeekRel(int32_t iOffset) + { + Element* pIterAfter(m_pIter + iOffset); + if (pIterAfter > m_pEnd) + { + return false; + } + + m_pIter = pIterAfter; + return true; + } + + bool SeekAbs(uint32_t uPosition) + { + Element* pIterAfter(m_pBegin + uPosition); + if (pIterAfter > m_pEnd) + { + return false; + } + + m_pIter = pIterAfter; + return true; + } +}; + +struct SDXBCInputBuffer + : SDXBCBufferBase +{ + SDXBCInputBuffer(const uint8_t* pBegin, const uint8_t* pEnd) + : SDXBCBufferBase(pBegin, pEnd) + { + } + + bool Read(void* pElements, size_t uSize) + { + const uint8_t* pIterAfter(m_pIter + uSize); + if (pIterAfter > m_pEnd) + { + return false; + } + + memcpy(pElements, m_pIter, uSize); + + m_pIter = pIterAfter; + return true; + } +}; + +struct SDXBCOutputBuffer + : SDXBCBufferBase +{ + SDXBCOutputBuffer(uint8_t* pBegin, uint8_t* pEnd) + : SDXBCBufferBase(pBegin, pEnd) + { + } + + bool Write(const void* pElements, size_t uSize) + { + uint8_t* pIterAfter(m_pIter + uSize); + if (pIterAfter > m_pEnd) + { + return false; + } + + memcpy(m_pIter, pElements, uSize); + + m_pIter = pIterAfter; + return true; + } +}; + +template +inline bool DXBCReadAs(S& kStream, External& kValue) +{ + Internal kInternal; + bool bResult(kStream.Read(&kInternal, sizeof(Internal))); + kValue = static_cast(DXBCSwapBytes(kInternal)); + return bResult; +} + +template +inline bool DXBCWriteAs(S& kStream, Internal kValue) +{ + Internal kInternal(DXBCSwapBytes(kValue)); + return kStream.Write(&kInternal, sizeof(Internal)); +} + +template +bool DXBCReadUint8 (S& kStream, T& kValue) { return DXBCReadAs(kStream, kValue); } +template +bool DXBCReadUint16(S& kStream, T& kValue) { return DXBCReadAs(kStream, kValue); } +template +bool DXBCReadUint32(S& kStream, T& kValue) { return DXBCReadAs(kStream, kValue); } + +template +bool DXBCWriteUint8 (S& kStream, uint8_t kValue) { return DXBCWriteAs(kStream, kValue); } +template +bool DXBCWriteUint16(S& kStream, uint16_t kValue) { return DXBCWriteAs(kStream, kValue); } +template +bool DXBCWriteUint32(S& kStream, uint32_t kValue) { return DXBCWriteAs(kStream, kValue); } + +template +bool DXBCCopy(O& kOutput, I& kInput, size_t uSize) +{ + char acBuffer[1024]; + while (uSize > 0) + { + size_t uToCopy(std::min(uSize, sizeof(acBuffer))); + if (!kInput.Read(acBuffer, uToCopy) || + !kOutput.Write(acBuffer, uToCopy)) + { + return false; + } + uSize -= uToCopy; + } + return true; +} + +enum +{ + DXBC_SIZE_POSITION = 6 * 4, + DXBC_HEADER_SIZE = 7 * 4, + DXBC_CHUNK_HEADER_SIZE = 2 * 4, + DXBC_MAX_NUM_CHUNKS_IN = 128, + DXBC_MAX_NUM_CHUNKS_OUT = 8, + DXBC_OUT_CHUNKS_INDEX_SIZE = (1 + 1 + DXBC_MAX_NUM_CHUNKS_OUT) * 4, + DXBC_OUT_FIXED_SIZE = DXBC_HEADER_SIZE + DXBC_OUT_CHUNKS_INDEX_SIZE, +}; + +inline void DXBCSizeGLSLChunk(uint32_t& uGLSLChunkSize, uint32_t& uNumSamplers, uint32_t& uGLSLSourceSize, const Shader* pShader) +{ + enum + { + GLSL_HEADER_SIZE = 4 * 8, // {uint32 uNumSamplers; uint32 uNumImports; uint32 uNumExports; uint32 uInputHash;uint32 uResources; uint32 ui32Thread_x; uint32 ui32Thread_y; uint32 ui32Thread_z} + GLSL_SAMPLER_SIZE = 4 * 2, // {uint32 uTexture; uint32 uSampler;} + GLSL_SYMBOL_SIZE = 4 * 3, // {uint32 uType; uint32 uID; uint32 uValue} + //extend for metal compute UAV type + GLSL_UAV_RESOURCES_AREA = 4 * 2, //{uint32 uResource; uint32 eBindArea} + }; + + // Only texture registers that are used are written + uNumSamplers = 0; + for (uint32_t uTexture = 0; uTexture < MAX_RESOURCE_BINDINGS; ++uTexture) + { + if (pShader->reflection.aui32SamplerMap[uTexture] != MAX_RESOURCE_BINDINGS) + { + ++uNumSamplers; + } + } + + //uint32_t uNumSymbols( + // pShader->reflection.ui32NumImports + + // pShader->reflection.ui32NumExports); + uint32_t uNumSymbols(0); // always 0 + uint32_t uNumResources(pShader->reflection.ui32NumResourceBindings); + + uint32_t uGLSLInfoSize( + DXBC_CHUNK_HEADER_SIZE + + GLSL_HEADER_SIZE + + uNumSamplers * GLSL_SAMPLER_SIZE + + uNumSymbols * GLSL_SYMBOL_SIZE + + uNumResources * GLSL_UAV_RESOURCES_AREA + ); + uGLSLSourceSize = (uint32_t)strlen(pShader->sourceCode) + 1; + uGLSLChunkSize = uGLSLInfoSize + uGLSLSourceSize; + uGLSLChunkSize += DXBC_BASE_ALIGNMENT - 1 - (uGLSLChunkSize - 1) % DXBC_BASE_ALIGNMENT; +} + +inline uint32_t DXBCSizeOutputChunk(uint32_t uCode, uint32_t uSizeIn) +{ + uint32_t uSizeOut; + switch (uCode) + { + case FOURCC_RDEF: + case FOURCC_ISGN: + case FOURCC_OSGN: + case FOURCC_PCSG: + case FOURCC_OSG1: + case FOURCC_ISG1: + // Preserve entire chunk + uSizeOut = uSizeIn; + break; + case FOURCC_SHDR: + case FOURCC_SHEX: + // Only keep the shader version + uSizeOut = uSizeIn < 4u ? uSizeIn : 4u; + break; + default: + // Discard the chunk + uSizeOut = 0; + break; + } + + return uSizeOut + DXBC_BASE_ALIGNMENT - 1 - (uSizeOut - 1) % DXBC_BASE_ALIGNMENT; +} + +template +size_t DXBCGetCombinedSize(I& kDXBCInput, const Shader* pShader) +{ + uint32_t uNumChunksIn; + if (!kDXBCInput.SeekAbs(DXBC_HEADER_SIZE) || + !DXBCReadUint32(kDXBCInput, uNumChunksIn)) + { + return 0; + } + + uint32_t auChunkOffsetsIn[DXBC_MAX_NUM_CHUNKS_IN]; + for (uint32_t uChunk = 0; uChunk < uNumChunksIn; ++uChunk) + { + if (!DXBCReadUint32(kDXBCInput, auChunkOffsetsIn[uChunk])) + { + return 0; + } + } + + uint32_t uNumChunksOut(0); + uint32_t uOutSize(DXBC_OUT_FIXED_SIZE); + for (uint32_t uChunk = 0; uChunk < uNumChunksIn && uNumChunksOut < DXBC_MAX_NUM_CHUNKS_OUT; ++uChunk) + { + uint32_t uChunkCode, uChunkSizeIn; + if (!kDXBCInput.SeekAbs(auChunkOffsetsIn[uChunk]) || + !DXBCReadUint32(kDXBCInput, uChunkCode) || + !DXBCReadUint32(kDXBCInput, uChunkSizeIn)) + { + return 0; + } + + uint32_t uChunkSizeOut(DXBCSizeOutputChunk(uChunkCode, uChunkSizeIn)); + if (uChunkSizeOut > 0) + { + uOutSize += DXBC_CHUNK_HEADER_SIZE + uChunkSizeOut; + } + } + + uint32_t uNumSamplers, uGLSLSourceSize, uGLSLChunkSize; + DXBCSizeGLSLChunk(uGLSLChunkSize, uNumSamplers, uGLSLSourceSize, pShader); + uOutSize += uGLSLChunkSize; + + return uOutSize; +} + +template +bool DXBCCombineWithGLSL(I& kInput, O& kOutput, const Shader* pShader) +{ + uint32_t uNumChunksIn; + if (!DXBCCopy(kOutput, kInput, DXBC_HEADER_SIZE) || + !DXBCReadUint32(kInput, uNumChunksIn) || + uNumChunksIn > DXBC_MAX_NUM_CHUNKS_IN) + { + return false; + } + + uint32_t auChunkOffsetsIn[DXBC_MAX_NUM_CHUNKS_IN]; + for (uint32_t uChunk = 0; uChunk < uNumChunksIn; ++uChunk) + { + if (!DXBCReadUint32(kInput, auChunkOffsetsIn[uChunk])) + { + return false; + } + } + + uint32_t auZeroChunkIndex[DXBC_OUT_CHUNKS_INDEX_SIZE] = {0}; + if (!kOutput.Write(auZeroChunkIndex, DXBC_OUT_CHUNKS_INDEX_SIZE)) + { + return false; + } + + // Copy required input chunks just after the chunk index + uint32_t uOutSize(DXBC_OUT_FIXED_SIZE); + uint32_t uNumChunksOut(0); + uint32_t auChunkOffsetsOut[DXBC_MAX_NUM_CHUNKS_OUT]; + for (uint32_t uChunk = 0; uChunk < uNumChunksIn; ++uChunk) + { + uint32_t uChunkCode, uChunkSizeIn; + if (!kInput.SeekAbs(auChunkOffsetsIn[uChunk]) || + !DXBCReadUint32(kInput, uChunkCode) || + !DXBCReadUint32(kInput, uChunkSizeIn)) + { + return false; + } + + // Filter only input chunks of the specified types + uint32_t uChunkSizeOut(DXBCSizeOutputChunk(uChunkCode, uChunkSizeIn)); + if (uChunkSizeOut > 0) + { + if (uNumChunksOut >= DXBC_MAX_NUM_CHUNKS_OUT) + { + return false; + } + + if (!DXBCWriteUint32(kOutput, uChunkCode) || + !DXBCWriteUint32(kOutput, uChunkSizeOut) || + !DXBCCopy(kOutput, kInput, uChunkSizeOut)) + { + return false; + } + + auChunkOffsetsOut[uNumChunksOut] = uOutSize; + ++uNumChunksOut; + uOutSize += DXBC_CHUNK_HEADER_SIZE + uChunkSizeOut; + } + } + // Write GLSL chunk + uint32_t uGLSLChunkOffset(uOutSize); + uint32_t uGLSLChunkSize, uNumSamplers, uGLSLSourceSize; + DXBCSizeGLSLChunk(uGLSLChunkSize, uNumSamplers, uGLSLSourceSize, pShader); + if (!DXBCWriteUint32(kOutput, (uint32_t)FOURCC_GLSL) || + !DXBCWriteUint32(kOutput, uGLSLChunkSize) || + !DXBCWriteUint32(kOutput, uNumSamplers) || + !DXBCWriteUint32(kOutput, 0) || + !DXBCWriteUint32(kOutput, 0) || + !DXBCWriteUint32(kOutput, 0) || + /*!DXBCWriteUint32(kOutput, pShader->reflection.ui32NumImports) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumExports) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32InputHash)*/ + !DXBCWriteUint32(kOutput, pShader->reflection.ui32NumResourceBindings) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32Thread_x) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32Thread_y) || + !DXBCWriteUint32(kOutput, pShader->reflection.ui32Thread_z)) + { + return false; + } + for (uint32_t uTexture = 0; uTexture < MAX_RESOURCE_BINDINGS; ++uTexture) + { + uint32_t uSampler(pShader->reflection.aui32SamplerMap[uTexture]); + if (uSampler != MAX_RESOURCE_BINDINGS) + { + if (!DXBCWriteUint32(kOutput, uTexture) || + !DXBCWriteUint32(kOutput, uSampler)) + { + return false; + } + } + } + //for (uint32_t uSymbol = 0; uSymbol < pShader->reflection.ui32NumImports; ++uSymbol) + //{ + // if (!DXBCWriteUint32(kOutput, pShader->reflection.psImports[uSymbol].eType) || + // !DXBCWriteUint32(kOutput, pShader->reflection.psImports[uSymbol].ui32ID) || + // !DXBCWriteUint32(kOutput, pShader->reflection.psImports[uSymbol].ui32Value)) + // return false; + //} + //for (uint32_t uSymbol = 0; uSymbol < pShader->reflection.ui32NumExports; ++uSymbol) + //{ + // if (!DXBCWriteUint32(kOutput, pShader->reflection.psExports[uSymbol].eType) || + // !DXBCWriteUint32(kOutput, pShader->reflection.psExports[uSymbol].ui32ID) || + // !DXBCWriteUint32(kOutput, pShader->reflection.psExports[uSymbol].ui32Value)) + // return false; + //} + for (uint32_t uResource = 0; uResource < pShader->reflection.ui32NumResourceBindings; ++uResource) + { + ResourceBinding* rb = pShader->reflection.psResourceBindings + uResource; + if (uResource != MAX_RESOURCE_BINDINGS) + { + if (!DXBCWriteUint32(kOutput, uResource) || + !DXBCWriteUint32(kOutput, rb->eBindArea)) + { + return false; + } + } + } + + if (!kOutput.Write(pShader->sourceCode, uGLSLSourceSize)) + { + return false; + } + uOutSize += uGLSLChunkSize; + + // Write total size and chunk index + if (!kOutput.SeekAbs(DXBC_SIZE_POSITION) || + !DXBCWriteUint32(kOutput, uOutSize) || + !kOutput.SeekAbs(DXBC_HEADER_SIZE) || + !DXBCWriteUint32(kOutput, uNumChunksOut + 1)) + { + return false; + } + for (uint32_t uChunk = 0; uChunk < uNumChunksOut; ++uChunk) + { + if (!DXBCWriteUint32(kOutput, auChunkOffsetsOut[uChunk])) + { + return false; + } + } + DXBCWriteUint32(kOutput, uGLSLChunkOffset); + + return true; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/include/pstdint.h b/Code/Tools/HLSLCrossCompilerMETAL/include/pstdint.h new file mode 100644 index 0000000000..6998242aa1 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/include/pstdint.h @@ -0,0 +1,801 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2011 Paul Hsieh + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + **************************************************************************** + * + * Version 0.1.12 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#ifndef uint8_t +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#ifndef int8_t +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#ifndef uint16_t +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#ifndef int16_t +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#ifndef uint32_t +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#ifndef int32_t +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif + +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are not + * defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) + +int main () { + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/jni/Android.mk b/Code/Tools/HLSLCrossCompilerMETAL/jni/Android.mk new file mode 100644 index 0000000000..66e2bb4ecf --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/jni/Android.mk @@ -0,0 +1,32 @@ +# +# Android Makefile conversion +# +# Leander Beernaert +# +# How to build: $ANDROID_NDK/ndk-build +# +VERSION=1.17 + +LOCAL_PATH := $(call my-dir)/../ + +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm +LOCAL_ARM_NEON := true + +LOCAL_MODULE := HLSLcc + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/src/cbstring +LOCAL_CFLAGS += -Wall -W +# For dynamic library +#LOCAL_CFLAGS += -DHLSLCC_DYNLIB +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/src/*.c) \ + $(wildcard $(LOCAL_PATH)/src/cbstring/*.c) \ + $(wildcard $(LOCAL_PATH)/src/internal_includes/*.c) +#LOCAL_LDLIBS += -lGLESv3 + +include $(BUILD_STATIC_LIBRARY) + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/jni/Application.mk b/Code/Tools/HLSLCrossCompilerMETAL/jni/Application.mk new file mode 100644 index 0000000000..a8ae0839b1 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/jni/Application.mk @@ -0,0 +1,3 @@ +APP_PLATFORM := android-18 +APP_ABI := armeabi-v7a +APP_OPTIM := release diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/android-armeabi-v7a/libHLSLcc.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/android-armeabi-v7a/libHLSLcc.a new file mode 100644 index 0000000000..79305b66b7 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/android-armeabi-v7a/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3469d419dc589eb7a68be97885d7a55b8b0bbbffd74c5c1586959be4698fb273 +size 1046940 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/ios/libHLSLcc.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/ios/libHLSLcc.a new file mode 100644 index 0000000000..7ecdd304eb --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/ios/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98fbcc0779c4a400530ad643e1125727c50fdbf01912f059ca296153f209eec5 +size 466488 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc.a new file mode 100644 index 0000000000..c76e85704a --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:270583c8762539856bf9f7c7cccf743c37db7fd4128cabd8fdfcfe3586177e27 +size 360488 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc_d.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc_d.a new file mode 100644 index 0000000000..ee23387576 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/linux/libHLSLcc_d.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85f1fcddb62db461ff1012f91c38d323591a220ef3f6c1e41277161a43959333 +size 1139822 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc.a new file mode 100644 index 0000000000..85bf31eed4 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af9216c54d23dd3754f7ae18d56b97ae256eb29a0046d8e0d2a0716054d8c230 +size 218888 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc_d.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc_d.a new file mode 100644 index 0000000000..00095a3615 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/mac/libHLSLcc_d.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a07bec349614cdd3e40c3577bddace1203148016f9276c7ef807bdbc37dcabf +size 671232 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc.a new file mode 100644 index 0000000000..c7b92fcc1e --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88acec4cedad5699900ec2d1a3ce83ab5e9365ebea4b4af0ababba562382f399 +size 296852 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc_d.a b/Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc_d.a new file mode 100644 index 0000000000..29dd7fbf7a --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/steamos/libHLSLcc_d.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c0625b7f534df5817646dd1335f9d7916389f27a83b7d118fadab504064d910 +size 1144250 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Debug/libHLSLcc.lib b/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Debug/libHLSLcc.lib new file mode 100644 index 0000000000..311dec443e --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Debug/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4d128256b757a7e800514482f1278348b489db53be2548b7770d701eece7ea9 +size 1022450 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Release/libHLSLcc.lib b/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Release/libHLSLcc.lib new file mode 100644 index 0000000000..d7fda333b7 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/Release/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d037d414fea62daf076b41ad1a0ffbb451fcaf4443f8b2f721166a4a33fe5865 +size 632236 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/libHLSLcc.lib b/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/libHLSLcc.lib new file mode 100644 index 0000000000..d531d2635f --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/win32/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e8d6023a2afd3db8f8bc6033db3d937d5a9ec635a9005bbc0dca121a196b2bb +size 428768 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/win64/Release/libHLSLcc.lib b/Code/Tools/HLSLCrossCompilerMETAL/lib/win64/Release/libHLSLcc.lib new file mode 100644 index 0000000000..d167e54c31 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/win64/Release/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7d890fdabc3b8cb4f61e10140090455bae656ec6d3fc8fa6460d96435120186 +size 809218 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/lib/win64/libHLSLcc.lib b/Code/Tools/HLSLCrossCompilerMETAL/lib/win64/libHLSLcc.lib new file mode 100644 index 0000000000..5135e7e081 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/lib/win64/libHLSLcc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caa74b7cebff2b35d0db9bb8b2eb70065ca7f5816a93004e8fa195aabefefe18 +size 600034 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/license.txt b/Code/Tools/HLSLCrossCompilerMETAL/license.txt new file mode 100644 index 0000000000..e20caeefef --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/license.txt @@ -0,0 +1,52 @@ +Copyright (c) 2012 James Jones +All Rights Reserved. + +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. + +This software makes use of the bstring library which is provided under the following license: + +Copyright (c) 2002-2008 Paul Hsieh +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 bstrlib 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 OWNER 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. + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/README b/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/README new file mode 100644 index 0000000000..7531c049a6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/README @@ -0,0 +1,247 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ + +Welcome to cJSON. + +cJSON aims to be the dumbest possible parser that you can get your job done with. +It's a single file of C, and a single header file. + +JSON is described best here: http://www.json.org/ +It's like XML, but fat-free. You use it to move data around, store things, or just +generally represent your program's state. + + +First up, how do I build? +Add cJSON.c to your project, and put cJSON.h somewhere in the header search path. +For example, to build the test app: + +gcc cJSON.c test.c -o test -lm +./test + + +As a library, cJSON exists to take away as much legwork as it can, but not get in your way. +As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it +in one of two modes: Auto and Manual. Let's have a quick run-through. + + +I lifted some JSON from this page: http://www.json.org/fatfree.html +That page inspired me to write cJSON, which is a parser that tries to share the same +philosophy as JSON itself. Simple, dumb, out of the way. + +Some JSON: +{ + "name": "Jack (\"Bee\") Nimble", + "format": { + "type": "rect", + "width": 1920, + "height": 1080, + "interlace": false, + "frame rate": 24 + } +} + +Assume that you got this from a file, a webserver, or magic JSON elves, whatever, +you have a char * to it. Everything is a cJSON struct. +Get it parsed: + cJSON *root = cJSON_Parse(my_json_string); + +This is an object. We're in C. We don't have objects. But we do have structs. +What's the framerate? + + cJSON *format = cJSON_GetObjectItem(root,"format"); + int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint; + + +Want to change the framerate? + cJSON_GetObjectItem(format,"frame rate")->valueint=25; + +Back to disk? + char *rendered=cJSON_Print(root); + +Finished? Delete the root (this takes care of everything else). + cJSON_Delete(root); + +That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers +before you dereference them. If you want to see how you'd build this struct in code? + cJSON *root,*fmt; + root=cJSON_CreateObject(); + cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); + cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject()); + cJSON_AddStringToObject(fmt,"type", "rect"); + cJSON_AddNumberToObject(fmt,"width", 1920); + cJSON_AddNumberToObject(fmt,"height", 1080); + cJSON_AddFalseToObject (fmt,"interlace"); + cJSON_AddNumberToObject(fmt,"frame rate", 24); + +Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup. +Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and +a few from elsewhere. + +What about manual mode? First up you need some detail. +Let's cover how the cJSON objects represent the JSON data. +cJSON doesn't distinguish arrays from objects in handling; just type. +Each cJSON has, potentially, a child, siblings, value, a name. + +The root object has: Object Type and a Child +The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: +Sibling has type Object, name "format", and a child. +That child has type String, name "type", value "rect", and a sibling: +Sibling has type Number, name "width", value 1920, and a sibling: +Sibling has type Number, name "height", value 1080, and a sibling: +Sibling hs type False, name "interlace", and a sibling: +Sibling has type Number, name "frame rate", value 24 + +Here's the structure: +typedef struct cJSON { + struct cJSON *next,*prev; + struct cJSON *child; + + int type; + + char *valuestring; + int valueint; + double valuedouble; + + char *string; +} cJSON; + +By default all values are 0 unless set by virtue of being meaningful. + +next/prev is a doubly linked list of siblings. next takes you to your sibling, +prev takes you back from your sibling to you. +Only objects and arrays have a "child", and it's the head of the doubly linked list. +A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0. +The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in +cJSON.h + +A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read +valuedouble. + +Any entry which is in the linked list which is the child of an object will have a "string" +which is the "name" of the entry. When I said "name" in the above example, that's "string". +"string" is the JSON name for the 'variable name' if you will. + +Now you can trivially walk the lists, recursively, and parse as you please. +You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take +the root object, and traverse the structure (which is, formally, an N-tree), +and tokenise as you please. If you wanted to build a callback style parser, this is how +you'd do it (just an example, since these things are very specific): + +void parse_and_callback(cJSON *item,const char *prefix) +{ + while (item) + { + char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2); + sprintf(newprefix,"%s/%s",prefix,item->name); + int dorecurse=callback(newprefix, item->type, item); + if (item->child && dorecurse) parse_and_callback(item->child,newprefix); + item=item->next; + free(newprefix); + } +} + +The prefix process will build you a separated list, to simplify your callback handling. +The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or +let you invoke it per-item. For the item above, your callback might look like this: + +int callback(const char *name,int type,cJSON *item) +{ + if (!strcmp(name,"name")) { /* populate name */ } + else if (!strcmp(name,"format/type") { /* handle "rect" */ } + else if (!strcmp(name,"format/width") { /* 800 */ } + else if (!strcmp(name,"format/height") { /* 600 */ } + else if (!strcmp(name,"format/interlace") { /* false */ } + else if (!strcmp(name,"format/frame rate") { /* 24 */ } + return 1; +} + +Alternatively, you might like to parse iteratively. +You'd use: + +void parse_object(cJSON *item) +{ + int i; for (i=0;ichild; + while (subitem) + { + // handle subitem + if (subitem->child) parse_object(subitem->child); + + subitem=subitem->next; + } +} + +Of course, this should look familiar, since this is just a stripped-down version +of the callback-parser. + +This should cover most uses you'll find for parsing. The rest should be possible +to infer.. and if in doubt, read the source! There's not a lot of it! ;) + + +In terms of constructing JSON data, the example code above is the right way to do it. +You can, of course, hand your sub-objects to other functions to populate. +Also, if you find a use for it, you can manually build the objects. +For instance, suppose you wanted to build an array of objects? + +cJSON *objects[24]; + +cJSON *Create_array_of_anything(cJSON **items,int num) +{ + int i;cJSON *prev, *root=cJSON_CreateArray(); + for (i=0;i<24;i++) + { + if (!i) root->child=objects[i]; + else prev->next=objects[i], objects[i]->prev=prev; + prev=objects[i]; + } + return root; +} + +and simply: Create_array_of_anything(objects,24); + +cJSON doesn't make any assumptions about what order you create things in. +You can attach the objects, as above, and later add children to each +of those objects. + +As soon as you call cJSON_Print, it renders the structure to text. + + + +The test.c code shows how to handle a bunch of typical cases. If you uncomment +the code, it'll load, parse and print a bunch of test files, also from json.org, +which are more complex than I'd care to try and stash into a const char array[]. + + +Enjoy cJSON! + + +- Dave Gamble, Aug 2009 diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.c b/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.c new file mode 100644 index 0000000000..56fb753ee8 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.c @@ -0,0 +1,578 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ +// Modifications copyright Amazon.com, Inc. or its affiliates. + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" +#include + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +AZ_PUSH_DISABLE_WARNING(4232, "-Wunknown-warning-option") // address of malloc/free are not static +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; +AZ_POP_DISABLE_WARNING + +static char* cJSON_strdup(const char* str) +{ + size_t len = strlen(str) + 1; + char* copy = (char*)cJSON_malloc(len); + + if (!copy) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + /* Could use sscanf for this? */ + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + sscanf(ptr+3,"%4x",&uc2);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str; + token = *ptr; + while (token && ++len) + { + if (strchr("\"\\\b\f\n\r\t",token)) len++; + else if (token<32) len+=5; + ptr++; + token = *ptr; + } + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item = cJSON_New_Item(); + if (!new_item) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=(int)strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item = cJSON_New_Item(); + if (!new_item) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + out=(char*)cJSON_malloc(fmt?depth+3:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=(int)(strlen(ret)+strlen(str))+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.h b/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.h new file mode 100644 index 0000000000..50ae02b6f9 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/cjson/cJSON.h @@ -0,0 +1,142 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ +// Modifications copyright Amazon.com, Inc. or its affiliates + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/compilerStandalone.cpp b/Code/Tools/HLSLCrossCompilerMETAL/offline/compilerStandalone.cpp new file mode 100644 index 0000000000..eaed596a00 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/compilerStandalone.cpp @@ -0,0 +1,825 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "hlslcc.hpp" +#include "stdlib.h" +#include "stdio.h" +#include "bstrlib.h" +#include +#include +#include "hash.h" +#include "serializeReflection.h" +#include "hlslcc_bin.hpp" + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "timer.h" + +#if defined(_WIN32) && !defined(PORTABLE) +#define VALIDATE_OUTPUT +#endif + +#if defined(VALIDATE_OUTPUT) +#if defined(_WIN32) +#include +#include + +#pragma comment(lib, "opengl32.lib") + +typedef char GLcharARB; /* native character */ +typedef unsigned int GLhandleARB; /* shader object handle */ +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +typedef void (WINAPI * PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB(WINAPI * PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (WINAPI * PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length); +typedef void (WINAPI * PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef void (WINAPI * PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (WINAPI * PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef GLhandleARB(WINAPI * PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (WINAPI * PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (WINAPI * PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (WINAPI * PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (WINAPI * PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei* length, GLcharARB* infoLog); + +static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +static PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +static PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +static PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +static PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +static PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +static PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +static PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; + +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 + +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 + +typedef HGLRC(WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int* attribList); +static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + +void InitOpenGL() +{ + HGLRC rc; + + // setup minimal required GL + HWND wnd = CreateWindowA( + "STATIC", + "GL", + WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 16, 16, + NULL, NULL, + GetModuleHandle(NULL), NULL); + HDC dc = GetDC(wnd); + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL, + PFD_TYPE_RGBA, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 16, 0, + 0, PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int fmt = ChoosePixelFormat(dc, &pfd); + SetPixelFormat(dc, fmt, &pfd); + + rc = wglCreateContext(dc); + wglMakeCurrent(dc, rc); + + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); + + if (wglCreateContextAttribsARB) + { + const int OpenGLContextAttribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, +#if defined(_DEBUG) + //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, +#else + //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, +#endif + //WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, 0 + }; + + const HGLRC OpenGLContext = wglCreateContextAttribsARB(dc, 0, OpenGLContextAttribs); + + wglMakeCurrent(dc, OpenGLContext); + + wglDeleteContext(rc); + + rc = OpenGLContext; + } + + glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)wglGetProcAddress("glDeleteObjectARB"); + glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)wglGetProcAddress("glCreateShaderObjectARB"); + glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)wglGetProcAddress("glShaderSourceARB"); + glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)wglGetProcAddress("glCompileShaderARB"); + glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)wglGetProcAddress("glGetInfoLogARB"); + glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB"); + glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)wglGetProcAddress("glCreateProgramObjectARB"); + glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)wglGetProcAddress("glAttachObjectARB"); + glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)wglGetProcAddress("glLinkProgramARB"); + glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)wglGetProcAddress("glUseProgramObjectARB"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); +} +#endif + +void PrintSingleLineError(FILE* pFile, char* error) +{ + while (*error != '\0') + { + char* pLineEnd = strchr(error, '\n'); + if (pLineEnd == 0) + pLineEnd = error + strlen(error) - 1; + fwrite(error, 1, pLineEnd - error, pFile); + fwrite("\r", 1, 1, pFile); + error = pLineEnd + 1; + } +} + +int TryCompileShader(GLenum eShaderType, const char* inFilename, char* shader, double* pCompileTime, int useStdErr) +{ + GLint iCompileStatus; + GLuint hShader; + Timer_t timer; + + InitTimer(&timer); + + InitOpenGL(); + + hShader = glCreateShaderObjectARB(eShaderType); + glShaderSourceARB(hShader, 1, (const char **)&shader, NULL); + + ResetTimer(&timer); + glCompileShaderARB(hShader); + *pCompileTime = ReadTimer(&timer); + + /* Check it compiled OK */ + glGetObjectParameterivARB(hShader, GL_OBJECT_COMPILE_STATUS_ARB, &iCompileStatus); + + if (iCompileStatus != GL_TRUE) + { + FILE* errorFile = NULL; + GLint iInfoLogLength = 0; + char* pszInfoLog; + + glGetObjectParameterivARB(hShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iInfoLogLength); + + pszInfoLog = new char[iInfoLogLength]; + + printf("Error: Failed to compile GLSL shader\n"); + + glGetInfoLogARB(hShader, iInfoLogLength, NULL, pszInfoLog); + + printf(pszInfoLog); + + if (!useStdErr) + { + std::string filename; + filename += inFilename; + filename += "_compileErrors.txt"; + + //Dump to file + errorFile = fopen(filename.c_str(), "w"); + + fclose(errorFile); + } + else + { + // Present error to stderror with no "new lines" as required by remote shader compiler + fprintf(stderr, "%s(-) error: ", inFilename); + PrintSingleLineError(stderr, pszInfoLog); + fprintf(stderr, "\rshader: "); + PrintSingleLineError(stderr, shader); + } + + delete[] pszInfoLog; + + return 0; + } + + return 1; +} +#endif + +int fileExists(const char* path) +{ + FILE* shaderFile; + shaderFile = fopen(path, "rb"); + + if (shaderFile) + { + fclose(shaderFile); + return 1; + } + return 0; +} + +ShaderLang LanguageFromString(const char* str) +{ + if (strcmp(str, "es100") == 0) + { + return LANG_ES_100; + } + if (strcmp(str, "es300") == 0) + { + return LANG_ES_300; + } + if (strcmp(str, "es310") == 0) + { + return LANG_ES_310; + } + if (strcmp(str, "120") == 0) + { + return LANG_120; + } + if (strcmp(str, "130") == 0) + { + return LANG_130; + } + if (strcmp(str, "140") == 0) + { + return LANG_140; + } + if (strcmp(str, "150") == 0) + { + return LANG_150; + } + if (strcmp(str, "330") == 0) + { + return LANG_330; + } + if (strcmp(str, "400") == 0) + { + return LANG_400; + } + if (strcmp(str, "410") == 0) + { + return LANG_410; + } + if (strcmp(str, "420") == 0) + { + return LANG_420; + } + if (strcmp(str, "430") == 0) + { + return LANG_430; + } + if (strcmp(str, "440") == 0) + { + return LANG_440; + } + if (strcmp(str, "metal") == 0) + { + return LANG_METAL; + } + return LANG_DEFAULT; +} + +#define MAX_PATH_CHARS 256 +#define MAX_FXC_CMD_CHARS 1024 +#define MAX_DEBUG_READ_CHARS 512 + +typedef struct +{ + ShaderLang language; + + int flags; + + const char* shaderFile; + char* outputShaderFile; + + char* reflectPath; + + char cacheKey[MAX_PATH_CHARS]; + + int bUseFxc; + std::string fxcCmdLine; +} Options; + +void InitOptions(Options* psOptions) +{ + psOptions->language = LANG_DEFAULT; + psOptions->flags = 0; + psOptions->reflectPath = NULL; + + psOptions->shaderFile = NULL; + + psOptions->bUseFxc = 0; +} + +void PrintHelp() +{ + printf("Command line options:\n"); + + printf("\t-lang=X \t Language to use. e.g. es100 or 140 or metal.\n"); + printf("\t-flags=X \t The integer value of the HLSLCC_FLAGS to used.\n"); + printf("\t-reflect=X \t File to write reflection JSON to.\n"); + printf("\t-in=X \t Shader file to compile.\n"); + printf("\t-out=X \t File to write the compiled shader from -in to.\n"); + + printf("\t-hashout=[dir/]out-file-name \t Output file name is a hash of 'out-file-name', put in the directory 'dir'.\n"); + + printf("\t-fxc=\"CMD\" HLSL compiler command line. If specified the input shader will be first compiled through this command first and then the resulting bytecode translated.\n"); + + printf("\n"); +} + +int GetOptions(int argc, char** argv, Options* psOptions) +{ + int i; + int fullShaderChain = -1; + + InitOptions(psOptions); + + for (i = 1; i < argc; i++) + { + char *option; + + option = strstr(argv[i], "-help"); + if (option != NULL) + { + PrintHelp(); + return 0; + } + + option = strstr(argv[i], "-reflect="); + if (option != NULL) + { + psOptions->reflectPath = option + strlen("-reflect="); + } + + option = strstr(argv[i], "-lang="); + if (option != NULL) + { + psOptions->language = LanguageFromString((&option[strlen("-lang=")])); + } + + option = strstr(argv[i], "-flags="); + if (option != NULL) + { + psOptions->flags = atol(&option[strlen("-flags=")]); + } + + option = strstr(argv[i], "-in="); + if (option != NULL) + { + fullShaderChain = 0; + psOptions->shaderFile = option + strlen("-in="); + if (!fileExists(psOptions->shaderFile)) + { + printf("Invalid path: %s\n", psOptions->shaderFile); + return 0; + } + } + + option = strstr(argv[i], "-out="); + if (option != NULL) + { + fullShaderChain = 0; + psOptions->outputShaderFile = option + strlen("-out="); + } + + option = strstr(argv[i], "-hashout"); + if (option != NULL) + { + fullShaderChain = 0; + psOptions->outputShaderFile = option + strlen("-hashout="); + + char* dir; + int64_t length; + + uint64_t hash = hash64((const uint8_t*)psOptions->outputShaderFile, (uint32_t)strlen(psOptions->outputShaderFile), 0); + + dir = strrchr(psOptions->outputShaderFile, '\\'); + + if (!dir) + { + dir = strrchr(psOptions->outputShaderFile, '//'); + } + + if (!dir) + { + length = 0; + } + else + { + length = (int)(dir - psOptions->outputShaderFile) + 1; + } + + for (i = 0; i < length; ++i) + { + psOptions->cacheKey[i] = psOptions->outputShaderFile[i]; + } + + //sprintf(psOptions->cacheKey, "%x%x", high, low); + sprintf(&psOptions->cacheKey[i], "%010llX", hash); + + psOptions->outputShaderFile = psOptions->cacheKey; + } + + option = strstr(argv[i], "-fxc="); + if (option != NULL) + { + char* cmdLine = option + strlen("-fxc="); + size_t cmdLineLen = strlen(cmdLine); + if (cmdLineLen == 0 || cmdLineLen + 1 >= MAX_FXC_CMD_CHARS) + return 0; + psOptions->fxcCmdLine = std::string(cmdLine, cmdLineLen); + psOptions->bUseFxc = 1; + } + } + + return 1; +} + +void *malloc_hook(size_t size) +{ + return malloc(size); +} +void *calloc_hook(size_t num, size_t size) +{ + return calloc(num, size); +} +void *realloc_hook(void *p, size_t size) +{ + return realloc(p, size); +} +void free_hook(void *p) +{ + free(p); +} + +int Run(const char* srcPath, const char* destPath, ShaderLang language, int flags, const char* reflectPath, Shader* shader, int useStdErr, [[maybe_unused]] const char *fxcCmdLine, [[maybe_unused]] const char *debugSrcPath) +{ + FILE* outputFile; + Shader tempShader; + Shader* result = shader ? shader : &tempShader; + Timer_t timer; + int compiledOK = 0; + double crossCompileTime = 0; + double glslCompileTime = 0; + + HLSLcc_SetMemoryFunctions(malloc_hook, calloc_hook, free_hook, realloc_hook); + + InitTimer(&timer); + + ResetTimer(&timer); + GlExtensions ext; + ext.ARB_explicit_attrib_location = 0; + ext.ARB_explicit_uniform_location = 0; + ext.ARB_shading_language_420pack = 0; + if (language == LANG_METAL) + { + compiledOK = TranslateHLSLFromFileToMETAL(srcPath, flags, language, result); + } + else + { + compiledOK = TranslateHLSLFromFileToGLSL(srcPath, flags, language, &ext, result); + } + crossCompileTime = ReadTimer(&timer); + + if (compiledOK) + { +#ifdef _DEBUG + bstring debugString = bfromcstr(result->sourceCode); + + bcatcstr(debugString, "\n\n// ------- DEBUG INFORMATION -------"); + + bformata(debugString, "\n// Shader Object Input: %s", srcPath); + bformata(debugString, "\n// Shader Output: %s", destPath); + if (debugSrcPath) + { + char debugStr[MAX_DEBUG_READ_CHARS]; + FILE* debugFile = fopen(debugSrcPath, "r"); + if (debugFile) + { + bformata(debugString, "\n// Shader HLSL Input: "); + while (!feof(debugFile)) + bformata(debugString, "// %s", fgets(debugStr, MAX_DEBUG_READ_CHARS, debugFile)); + fclose(debugFile); + } + } + if (fxcCmdLine) + bformata(debugString, "\n// FXC Command: %s", fxcCmdLine); + + result->sourceCode = bstr2cstr(debugString, '\0'); +#endif + printf("cc time: %.2f us\n", crossCompileTime); + +#if !defined(APPLE) + // https://msdn.microsoft.com/en-us/library/ms175782.aspx. As to disable the "("'n' format specifier disabled", 0)" assertion. + _set_printf_count_output(1); +#endif + + if (destPath) + { + //Dump to file + outputFile = fopen(destPath, "w"); + fprintf(outputFile, result->sourceCode); + + fclose(outputFile); + } + + if (reflectPath) + { + const char* jsonString = SerializeReflection(&result->reflection); + outputFile = fopen(reflectPath, "w"); + fprintf(outputFile, jsonString); + fclose(outputFile); + } + +#if defined(VALIDATE_OUTPUT) + if (language != LANG_METAL) + { + compiledOK = TryCompileShader(result->shaderType, destPath ? destPath : "", result->sourceCode, &glslCompileTime, useStdErr); + + if (compiledOK) + { + printf("glsl time: %.2f us\n", glslCompileTime); + } + } +#endif + + if (!shader) + FreeShader(result); + } + else if (useStdErr) + { + fprintf(stderr, "TranslateHLSLFromFile failed"); + } + + return compiledOK; +} + +struct SDXBCFile +{ + FILE* m_pFile; + + bool Read(void* pElements, size_t uSize) + { + return fread(pElements, 1, uSize, m_pFile) == uSize; + } + + bool Write(const void* pElements, size_t uSize) + { + return fwrite(pElements, 1, uSize, m_pFile) == uSize; + } + + bool SeekRel(int32_t iOffset) + { + return fseek(m_pFile, iOffset, SEEK_CUR) == 0; + } + + bool SeekAbs(uint32_t uPosition) + { + return fseek(m_pFile, uPosition, SEEK_SET) == 0; + } +}; + +int CombineDXBCWithGLSL(char* dxbcFileName, char* outputFileName, Shader* shader) +{ + SDXBCFile dxbcFile = { fopen(dxbcFileName, "rb") }; + SDXBCFile outputFile = { fopen(outputFileName, "wb") }; + + bool result = + dxbcFile.m_pFile != NULL && outputFile.m_pFile != NULL && + DXBCCombineWithGLSL(dxbcFile, outputFile, shader); + + if (dxbcFile.m_pFile != NULL) + fclose(dxbcFile.m_pFile); + if (outputFile.m_pFile != NULL) + fclose(outputFile.m_pFile); + + return result; +} + +#if !defined(_MSC_VER) +#define sprintf_s(dest, size, ...) sprintf(dest, __VA_ARGS__) +#endif + +#if defined(_WIN32) && defined(PORTABLE) + +DWORD FilterException(DWORD uExceptionCode) +{ + const char* szExceptionName; + char acTemp[10]; + switch (uExceptionCode) + { +#define _CASE(_Name) \ + case _Name: \ + szExceptionName = #_Name; \ + break; + _CASE(EXCEPTION_ACCESS_VIOLATION) + _CASE(EXCEPTION_DATATYPE_MISALIGNMENT) + _CASE(EXCEPTION_BREAKPOINT) + _CASE(EXCEPTION_SINGLE_STEP) + _CASE(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) + _CASE(EXCEPTION_FLT_DENORMAL_OPERAND) + _CASE(EXCEPTION_FLT_DIVIDE_BY_ZERO) + _CASE(EXCEPTION_FLT_INEXACT_RESULT) + _CASE(EXCEPTION_FLT_INVALID_OPERATION) + _CASE(EXCEPTION_FLT_OVERFLOW) + _CASE(EXCEPTION_FLT_STACK_CHECK) + _CASE(EXCEPTION_FLT_UNDERFLOW) + _CASE(EXCEPTION_INT_DIVIDE_BY_ZERO) + _CASE(EXCEPTION_INT_OVERFLOW) + _CASE(EXCEPTION_PRIV_INSTRUCTION) + _CASE(EXCEPTION_IN_PAGE_ERROR) + _CASE(EXCEPTION_ILLEGAL_INSTRUCTION) + _CASE(EXCEPTION_NONCONTINUABLE_EXCEPTION) + _CASE(EXCEPTION_STACK_OVERFLOW) + _CASE(EXCEPTION_INVALID_DISPOSITION) + _CASE(EXCEPTION_GUARD_PAGE) + _CASE(EXCEPTION_INVALID_HANDLE) + //_CASE(EXCEPTION_POSSIBLE_DEADLOCK) +#undef _CASE + default: + sprintf_s(acTemp, "0x%08X", uExceptionCode); + szExceptionName = acTemp; + } + + fprintf(stderr, "Hardware exception thrown (%s)\n", szExceptionName); + return 1; +} + +#endif + +const char* PatchHLSLShaderFile(const char* path) +{ + // Need to transform "half" into "min16float" so FXC preserve min precision to the operands. + static char patchedFileName[MAX_PATH_CHARS]; + const char* defines = "#define half min16float\n" + "#define half2 min16float2\n" + "#define half3 min16float3\n" + "#define half4 min16float4\n"; + + sprintf_s(patchedFileName, sizeof(patchedFileName), "%s.hlslPatched", path); + FILE* shaderFile = fopen(path, "rb"); + if (!shaderFile) + { + return NULL; + } + + FILE* patchedFile = fopen(patchedFileName, "wb"); + if (!patchedFile) + { + return NULL; + } + + // Get size of file + bool result = false; + fseek(shaderFile, 0, SEEK_END); + long size = ftell(shaderFile); + fseek(shaderFile, 0, SEEK_SET); + unsigned char* data = new unsigned char[size + 1]; // Extra byte for the '/0' character. + if (fread(data, 1, size, shaderFile) == size) + { + data[size] = '\0'; + fprintf(patchedFile, "%s%s", defines, data); + result = true; + } + + if (shaderFile) + { + fclose(shaderFile); + } + + if (patchedFile) + { + fclose(patchedFile); + } + + delete[] data; + return result ? patchedFileName : NULL; +} + +int main(int argc, char** argv) +{ + Options options; + +#if defined(_WIN32) && defined(PORTABLE) + __try + { +#endif + + if (!GetOptions(argc, argv, &options)) + { + return 1; + } + + if (options.bUseFxc) + { + char dxbcFileName[MAX_PATH_CHARS]; + char glslFileName[MAX_PATH_CHARS]; + char fullFxcCmdLine[MAX_FXC_CMD_CHARS]; + int retValue; + + if (options.flags & HLSLCC_FLAG_HALF_FLOAT_TRANSFORM) + { + options.shaderFile = PatchHLSLShaderFile(options.shaderFile); + if (!options.shaderFile) + { + return 1; + } + } + + sprintf_s(dxbcFileName, sizeof(dxbcFileName), "%s.dxbc", options.shaderFile); + sprintf_s(glslFileName, sizeof(glslFileName), "%s.patched", options.shaderFile); + + // Need to extract the path to the executable so we can enclose it in quotes + // in case it contains spaces. + const std::string fxcExeName = "fxc.exe"; + + // Case insensitive search + std::string::iterator fxcPos = std::search( + options.fxcCmdLine.begin(), options.fxcCmdLine.end(), + fxcExeName.begin(), fxcExeName.end(), + [](char ch1, char ch2) { return std::tolower(ch1) == std::tolower(ch2); } + ); + + if (fxcPos == options.fxcCmdLine.end()) + { + fprintf(stderr, "Could not find fxc.exe in command line"); + return 1; + } + + // Add the fxcExeName so it gets copied to the fxcExe path. + fxcPos += fxcExeName.length(); + std::string fxcExe(options.fxcCmdLine.begin(), fxcPos); + std::string fxcArguments(fxcPos, options.fxcCmdLine.end()); + +#if defined(APPLE) + fprintf(stderr, "fxc.exe cannot be executed on Mac"); + return 1; +#else + // Need an extra set of quotes around the full command line because the way "system" executes it using cmd. + sprintf_s(fullFxcCmdLine, sizeof(fullFxcCmdLine), "\"\"%s\" %s \"%s\" \"%s\"\"", fxcExe.c_str(), fxcArguments.c_str(), dxbcFileName, options.shaderFile); +#endif + + retValue = system(fullFxcCmdLine); + + if (retValue == 0) + { + Shader shader; + retValue = !Run(dxbcFileName, glslFileName, options.language, options.flags, options.reflectPath, &shader, 1, fullFxcCmdLine, options.shaderFile); + + if (retValue == 0) + { + retValue = !CombineDXBCWithGLSL(dxbcFileName, options.outputShaderFile, &shader); + FreeShader(&shader); + } + } + + remove(dxbcFileName); + remove(glslFileName); + if (options.flags & HLSLCC_FLAG_HALF_FLOAT_TRANSFORM) + { + // Removed the hlsl patched file that was created. + remove(options.shaderFile); + } + + return retValue; + } + else if (options.shaderFile) + { + if (!Run(options.shaderFile, options.outputShaderFile, options.language, options.flags, options.reflectPath, NULL, 0, NULL, NULL)) + { + return 1; + } + } + +#if defined(_WIN32) && defined(PORTABLE) + } + __except (FilterException(GetExceptionCode())) + { + return 1; + } +#endif + + + return 0; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/hash.h b/Code/Tools/HLSLCrossCompilerMETAL/offline/hash.h new file mode 100644 index 0000000000..e480417717 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/hash.h @@ -0,0 +1,128 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef HASH_H_ +#define HASH_H_ + +/* +-------------------------------------------------------------------- +mix -- mix 3 64-bit values reversibly. +mix() takes 48 machine instructions, but only 24 cycles on a superscalar + machine (like Intel's new MMX architecture). It requires 4 64-bit + registers for 4::2 parallelism. +All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of + (a,b,c), and all deltas of bottom bits were tested. All deltas were + tested both on random keys and on keys that were nearly all zero. + These deltas all cause every bit of c to change between 1/3 and 2/3 + of the time (well, only 113/400 to 287/400 of the time for some + 2-bit delta). These deltas all cause at least 80 bits to change + among (a,b,c) when the mix is run either forward or backward (yes it + is reversible). +This implies that a hash using mix64 has no funnels. There may be + characteristics with 3-bit deltas or bigger, I didn't test for + those. +-------------------------------------------------------------------- +*/ +#define mix64(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>43); \ + b -= c; b -= a; b ^= (a<<9); \ + c -= a; c -= b; c ^= (b>>8); \ + a -= b; a -= c; a ^= (c>>38); \ + b -= c; b -= a; b ^= (a<<23); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>35); \ + b -= c; b -= a; b ^= (a<<49); \ + c -= a; c -= b; c ^= (b>>11); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<18); \ + c -= a; c -= b; c ^= (b>>22); \ +} + +/* +-------------------------------------------------------------------- +hash64() -- hash a variable-length key into a 64-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 8-byte value +Returns a 64-bit value. Every bit of the key affects every bit of +the return value. No funnels. Every 1-bit and 2-bit delta achieves +avalanche. About 41+5len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 64 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i= 24) + { + a += (k[0] +((uint64_t)k[ 1]<< 8)+((uint64_t)k[ 2]<<16)+((uint64_t)k[ 3]<<24) + +((uint64_t)k[4 ]<<32)+((uint64_t)k[ 5]<<40)+((uint64_t)k[ 6]<<48)+((uint64_t)k[ 7]<<56)); + b += (k[8] +((uint64_t)k[ 9]<< 8)+((uint64_t)k[10]<<16)+((uint64_t)k[11]<<24) + +((uint64_t)k[12]<<32)+((uint64_t)k[13]<<40)+((uint64_t)k[14]<<48)+((uint64_t)k[15]<<56)); + c += (k[16] +((uint64_t)k[17]<< 8)+((uint64_t)k[18]<<16)+((uint64_t)k[19]<<24) + +((uint64_t)k[20]<<32)+((uint64_t)k[21]<<40)+((uint64_t)k[22]<<48)+((uint64_t)k[23]<<56)); + mix64(a,b,c); + k += 24; len -= 24; + } + + /*------------------------------------- handle the last 23 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 23: c+=((uint64_t)k[22]<<56); + case 22: c+=((uint64_t)k[21]<<48); + case 21: c+=((uint64_t)k[20]<<40); + case 20: c+=((uint64_t)k[19]<<32); + case 19: c+=((uint64_t)k[18]<<24); + case 18: c+=((uint64_t)k[17]<<16); + case 17: c+=((uint64_t)k[16]<<8); + /* the first byte of c is reserved for the length */ + case 16: b+=((uint64_t)k[15]<<56); + case 15: b+=((uint64_t)k[14]<<48); + case 14: b+=((uint64_t)k[13]<<40); + case 13: b+=((uint64_t)k[12]<<32); + case 12: b+=((uint64_t)k[11]<<24); + case 11: b+=((uint64_t)k[10]<<16); + case 10: b+=((uint64_t)k[ 9]<<8); + case 9: b+=((uint64_t)k[ 8]); + case 8: a+=((uint64_t)k[ 7]<<56); + case 7: a+=((uint64_t)k[ 6]<<48); + case 6: a+=((uint64_t)k[ 5]<<40); + case 5: a+=((uint64_t)k[ 4]<<32); + case 4: a+=((uint64_t)k[ 3]<<24); + case 3: a+=((uint64_t)k[ 2]<<16); + case 2: a+=((uint64_t)k[ 1]<<8); + case 1: a+=((uint64_t)k[ 0]); + /* case 0: nothing left to add */ + } + mix64(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.cpp b/Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.cpp new file mode 100644 index 0000000000..15fe8d5b96 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.cpp @@ -0,0 +1,207 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "serializeReflection.h" +#include "cJSON.h" +#include +#include + +void* jsonMalloc(size_t sz) +{ + return new char[sz]; +} +void jsonFree(void* ptr) +{ + char* charPtr = static_cast(ptr); + delete [] charPtr; +} + +static void AppendIntToString(std::string& str, uint32_t num) +{ + std::stringstream ss; + ss << num; + str += ss.str(); +} + +static void WriteInOutSignature(InOutSignature* psSignature, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "SemanticName", cJSON_CreateString(psSignature->SemanticName)); + cJSON_AddItemToObject(obj, "ui32SemanticIndex", cJSON_CreateNumber(psSignature->ui32SemanticIndex)); + cJSON_AddItemToObject(obj, "eSystemValueType", cJSON_CreateNumber(psSignature->eSystemValueType)); + cJSON_AddItemToObject(obj, "eComponentType", cJSON_CreateNumber(psSignature->eComponentType)); + cJSON_AddItemToObject(obj, "ui32Register", cJSON_CreateNumber(psSignature->ui32Register)); + cJSON_AddItemToObject(obj, "ui32Mask", cJSON_CreateNumber(psSignature->ui32Mask)); + cJSON_AddItemToObject(obj, "ui32ReadWriteMask", cJSON_CreateNumber(psSignature->ui32ReadWriteMask)); +} + +static void WriteResourceBinding(ResourceBinding* psBinding, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psBinding->Name)); + cJSON_AddItemToObject(obj, "eType", cJSON_CreateNumber(psBinding->eType)); + cJSON_AddItemToObject(obj, "ui32BindPoint", cJSON_CreateNumber(psBinding->ui32BindPoint)); + cJSON_AddItemToObject(obj, "ui32BindCount", cJSON_CreateNumber(psBinding->ui32BindCount)); + cJSON_AddItemToObject(obj, "ui32Flags", cJSON_CreateNumber(psBinding->ui32Flags)); + cJSON_AddItemToObject(obj, "eDimension", cJSON_CreateNumber(psBinding->eDimension)); + cJSON_AddItemToObject(obj, "ui32ReturnType", cJSON_CreateNumber(psBinding->ui32ReturnType)); + cJSON_AddItemToObject(obj, "ui32NumSamples", cJSON_CreateNumber(psBinding->ui32NumSamples)); +} + +static void WriteShaderVar(ShaderVar* psVar, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psVar->Name)); + if(psVar->haveDefaultValue) + { + cJSON_AddItemToObject(obj, "aui32DefaultValues", cJSON_CreateIntArray((int*)psVar->pui32DefaultValues, psVar->ui32Size/4)); + } + cJSON_AddItemToObject(obj, "ui32StartOffset", cJSON_CreateNumber(psVar->ui32StartOffset)); + cJSON_AddItemToObject(obj, "ui32Size", cJSON_CreateNumber(psVar->ui32Size)); +} + +static void WriteConstantBuffer(ConstantBuffer* psCBuf, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psCBuf->Name)); + cJSON_AddItemToObject(obj, "ui32NumVars", cJSON_CreateNumber(psCBuf->ui32NumVars)); + + for(uint32_t i = 0; i < psCBuf->ui32NumVars; ++i) + { + std::string name; + name += "var"; + AppendIntToString(name, i); + + cJSON* varObj = cJSON_CreateObject(); + cJSON_AddItemToObject(obj, name.c_str(), varObj); + + WriteShaderVar(&psCBuf->asVars[i], varObj); + } + + cJSON_AddItemToObject(obj, "ui32TotalSizeInBytes", cJSON_CreateNumber(psCBuf->ui32TotalSizeInBytes)); +} + +static void WriteClassType(ClassType* psClassType, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psClassType->Name)); + cJSON_AddItemToObject(obj, "ui16ID", cJSON_CreateNumber(psClassType->ui16ID)); + cJSON_AddItemToObject(obj, "ui16ConstBufStride", cJSON_CreateNumber(psClassType->ui16ConstBufStride)); + cJSON_AddItemToObject(obj, "ui16Texture", cJSON_CreateNumber(psClassType->ui16Texture)); + cJSON_AddItemToObject(obj, "ui16Sampler", cJSON_CreateNumber(psClassType->ui16Sampler)); +} + +static void WriteClassInstance(ClassInstance* psClassInst, cJSON* obj) +{ + cJSON_AddItemToObject(obj, "Name", cJSON_CreateString(psClassInst->Name)); + cJSON_AddItemToObject(obj, "ui16ID", cJSON_CreateNumber(psClassInst->ui16ID)); + cJSON_AddItemToObject(obj, "ui16ConstBuf", cJSON_CreateNumber(psClassInst->ui16ConstBuf)); + cJSON_AddItemToObject(obj, "ui16ConstBufOffset", cJSON_CreateNumber(psClassInst->ui16ConstBufOffset)); + cJSON_AddItemToObject(obj, "ui16Texture", cJSON_CreateNumber(psClassInst->ui16Texture)); + cJSON_AddItemToObject(obj, "ui16Sampler", cJSON_CreateNumber(psClassInst->ui16Sampler)); +} + +const char* SerializeReflection(ShaderInfo* psReflection) +{ + cJSON* root; + + cJSON_Hooks hooks; + hooks.malloc_fn = jsonMalloc; + hooks.free_fn = jsonFree; + cJSON_InitHooks(&hooks); + + root=cJSON_CreateObject(); + cJSON_AddItemToObject(root, "ui32MajorVersion", cJSON_CreateNumber(psReflection->ui32MajorVersion)); + cJSON_AddItemToObject(root, "ui32MinorVersion", cJSON_CreateNumber(psReflection->ui32MinorVersion)); + + cJSON_AddItemToObject(root, "ui32NumInputSignatures", cJSON_CreateNumber(psReflection->ui32NumInputSignatures)); + + for(uint32_t i = 0; i < psReflection->ui32NumInputSignatures; ++i) + { + std::string name; + name += "input"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteInOutSignature(psReflection->psInputSignatures+i, obj); + } + + cJSON_AddItemToObject(root, "ui32NumOutputSignatures", cJSON_CreateNumber(psReflection->ui32NumOutputSignatures)); + + for(uint32_t i = 0; i < psReflection->ui32NumOutputSignatures; ++i) + { + std::string name; + name += "output"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteInOutSignature(psReflection->psOutputSignatures+i, obj); + } + + cJSON_AddItemToObject(root, "ui32NumResourceBindings", cJSON_CreateNumber(psReflection->ui32NumResourceBindings)); + + for(uint32_t i = 0; i < psReflection->ui32NumResourceBindings; ++i) + { + std::string name; + name += "resource"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteResourceBinding(psReflection->psResourceBindings+i, obj); + } + + cJSON_AddItemToObject(root, "ui32NumConstantBuffers", cJSON_CreateNumber(psReflection->ui32NumConstantBuffers)); + + for(uint32_t i = 0; i < psReflection->ui32NumConstantBuffers; ++i) + { + std::string name; + name += "cbuf"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteConstantBuffer(psReflection->psConstantBuffers+i, obj); + } + + //psThisPointerConstBuffer is a cache. Don't need to write this out. + //It just points to the $ThisPointer cbuffer within the psConstantBuffers array. + + for(uint32_t i = 0; i < psReflection->ui32NumClassTypes; ++i) + { + std::string name; + name += "classType"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteClassType(psReflection->psClassTypes+i, obj); + } + + for(uint32_t i = 0; i < psReflection->ui32NumClassInstances; ++i) + { + std::string name; + name += "classInst"; + AppendIntToString(name, i); + + cJSON* obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, name.c_str(), obj); + + WriteClassInstance(psReflection->psClassInstances+i, obj); + } + + //psReflection->aui32TableIDToTypeID + //psReflection->aui32ConstBufferBindpointRemap + + cJSON_AddItemToObject(root, "eTessPartitioning", cJSON_CreateNumber(psReflection->eTessPartitioning)); + cJSON_AddItemToObject(root, "eTessOutPrim", cJSON_CreateNumber(psReflection->eTessOutPrim)); + + + const char* jsonString = cJSON_Print(root); + + cJSON_Delete(root); + + return jsonString; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.h b/Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.h new file mode 100644 index 0000000000..c8c4175a6a --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/serializeReflection.h @@ -0,0 +1,11 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef SERIALIZE_REFLECTION_H_ +#define SERIALIZE_REFLECTION_H_ + +#include "hlslcc.h" + +const char* SerializeReflection(ShaderInfo* psReflection); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/timer.cpp b/Code/Tools/HLSLCrossCompilerMETAL/offline/timer.cpp new file mode 100644 index 0000000000..c707e1bfa8 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/timer.cpp @@ -0,0 +1,40 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "timer.h" + +void InitTimer(Timer_t* psTimer) +{ +#if defined(_WIN32) + QueryPerformanceFrequency(&psTimer->frequency); +#endif +} + +void ResetTimer(Timer_t* psTimer) +{ +#if defined(_WIN32) + QueryPerformanceCounter(&psTimer->startCount); +#else + gettimeofday(&psTimer->startCount, 0); +#endif +} + +/* Returns time in micro seconds */ +double ReadTimer(Timer_t* psTimer) +{ + double startTimeInMicroSec, endTimeInMicroSec; + +#if defined(_WIN32) + const double freq = (1000000.0 / psTimer->frequency.QuadPart); + QueryPerformanceCounter(&psTimer->endCount); + startTimeInMicroSec = psTimer->startCount.QuadPart * freq; + endTimeInMicroSec = psTimer->endCount.QuadPart * freq; +#else + gettimeofday(&psTimer->endCount, 0); + startTimeInMicroSec = (psTimer->startCount.tv_sec * 1000000.0) + psTimer->startCount.tv_usec; + endTimeInMicroSec = (psTimer->endCount.tv_sec * 1000000.0) + psTimer->endCount.tv_usec; +#endif + + return endTimeInMicroSec - startTimeInMicroSec; +} + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/offline/timer.h b/Code/Tools/HLSLCrossCompilerMETAL/offline/timer.h new file mode 100644 index 0000000000..3f4ea333fd --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/offline/timer.h @@ -0,0 +1,29 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TIMER_H +#define TIMER_H + +#ifdef _WIN32 +#include +#else +#include +#endif + +typedef struct +{ +#ifdef _WIN32 + LARGE_INTEGER frequency; + LARGE_INTEGER startCount; + LARGE_INTEGER endCount; +#else + struct timeval startCount; + struct timeval endCount; +#endif +} Timer_t; + +void InitTimer(Timer_t* psTimer); +void ResetTimer(Timer_t* psTimer); +double ReadTimer(Timer_t* psTimer); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.c b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.c new file mode 100644 index 0000000000..3f24fa3341 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.c @@ -0,0 +1,20 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bsafe.c + * + * This is an optional module that can be used to help enforce a safety + * standard based on pervasive usage of bstrlib. This file is not necessarily + * portable, however, it has been tested to work correctly with Intel's C/C++ + * compiler, WATCOM C/C++ v11.x and Microsoft Visual C++. + */ + +#include +#include +#include "bsafe.h" diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.h b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.h new file mode 100644 index 0000000000..3e18e33493 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bsafe.h @@ -0,0 +1,39 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bsafe.h + * + * This is an optional module that can be used to help enforce a safety + * standard based on pervasive usage of bstrlib. This file is not necessarily + * portable, however, it has been tested to work correctly with Intel's C/C++ + * compiler, WATCOM C/C++ v11.x and Microsoft Visual C++. + */ + +#ifndef BSTRLIB_BSAFE_INCLUDE +#define BSTRLIB_BSAFE_INCLUDE + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * (strncpy) (char *dst, const char *src, size_t n); +extern char * (strncat) (char *dst, const char *src, size_t n); +extern char * (strtok) (char *s1, const char *s2); +extern char * (strdup) (const char *s); + +#undef strcpy +#undef strcat +#define strcpy(a,b) bsafe_strcpy(a,b) +#define strcat(a,b) bsafe_strcat(a,b) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.c b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.c new file mode 100644 index 0000000000..2dc7b04840 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.c @@ -0,0 +1,1134 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstraux.c + * + * This file is not necessarily part of the core bstring library itself, but + * is just an auxilliary module which includes miscellaneous or trivial + * functions. + */ + +#include +#include +#include +#include +#include +#include "bstrlib.h" +#include "bstraux.h" + +/* bstring bTail (bstring b, int n) + * + * Return with a string of the last n characters of b. + */ +bstring bTail (bstring b, int n) { + if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL; + if (n >= b->slen) return bstrcpy (b); + return bmidstr (b, b->slen - n, n); +} + +/* bstring bHead (bstring b, int n) + * + * Return with a string of the first n characters of b. + */ +bstring bHead (bstring b, int n) { + if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL; + if (n >= b->slen) return bstrcpy (b); + return bmidstr (b, 0, n); +} + +/* int bFill (bstring a, char c, int len) + * + * Fill a given bstring with the character in parameter c, for a length n. + */ +int bFill (bstring b, char c, int len) { + if (b == NULL || len < 0 || (b->mlen < b->slen && b->mlen > 0)) return -__LINE__; + b->slen = 0; + return bsetstr (b, len, NULL, c); +} + +/* int bReplicate (bstring b, int n) + * + * Replicate the contents of b end to end n times and replace it in b. + */ +int bReplicate (bstring b, int n) { + return bpattern (b, n * b->slen); +} + +/* int bReverse (bstring b) + * + * Reverse the contents of b in place. + */ +int bReverse (bstring b) { +int i, n, m; +unsigned char t; + + if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__; + n = b->slen; + if (2 <= n) { + m = ((unsigned)n) >> 1; + n--; + for (i=0; i < m; i++) { + t = b->data[n - i]; + b->data[n - i] = b->data[i]; + b->data[i] = t; + } + } + return 0; +} + +/* int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) + * + * Insert a repeated sequence of a given character into the string at + * position pos for a length len. + */ +int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) { + if (b == NULL || b->slen < 0 || b->mlen < b->slen || pos < 0 || len <= 0) return -__LINE__; + + if (pos > b->slen + && 0 > bsetstr (b, pos, NULL, fill)) return -__LINE__; + + if (0 > balloc (b, b->slen + len)) return -__LINE__; + if (pos < b->slen) memmove (b->data + pos + len, b->data + pos, b->slen - pos); + memset (b->data + pos, c, len); + b->slen += len; + b->data[b->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bJustifyLeft (bstring b, int space) + * + * Left justify a string. + */ +int bJustifyLeft (bstring b, int space) { +int j, i, s, t; +unsigned char c = (unsigned char) space; + + if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__; + if (space != (int) c) return BSTR_OK; + + for (s=j=i=0; i < b->slen; i++) { + t = s; + s = c != (b->data[j] = b->data[i]); + j += (t|s); + } + if (j > 0 && b->data[j-1] == c) j--; + + b->data[j] = (unsigned char) '\0'; + b->slen = j; + return BSTR_OK; +} + +/* int bJustifyRight (bstring b, int width, int space) + * + * Right justify a string to within a given width. + */ +int bJustifyRight (bstring b, int width, int space) { +int ret; + if (width <= 0) return -__LINE__; + if (0 > (ret = bJustifyLeft (b, space))) return ret; + if (b->slen <= width) + return bInsertChrs (b, 0, width - b->slen, (unsigned char) space, (unsigned char) space); + return BSTR_OK; +} + +/* int bJustifyCenter (bstring b, int width, int space) + * + * Center a string's non-white space characters to within a given width by + * inserting whitespaces at the beginning. + */ +int bJustifyCenter (bstring b, int width, int space) { +int ret; + if (width <= 0) return -__LINE__; + if (0 > (ret = bJustifyLeft (b, space))) return ret; + if (b->slen <= width) + return bInsertChrs (b, 0, (width - b->slen + 1) >> 1, (unsigned char) space, (unsigned char) space); + return BSTR_OK; +} + +/* int bJustifyMargin (bstring b, int width, int space) + * + * Stretch a string to flush against left and right margins by evenly + * distributing additional white space between words. If the line is too + * long to be margin justified, it is left justified. + */ +int bJustifyMargin (bstring b, int width, int space) { +struct bstrList * sl; +int i, l, c; + + if (b == NULL || b->slen < 0 || b->mlen == 0 || b->mlen < b->slen) return -__LINE__; + if (NULL == (sl = bsplit (b, (unsigned char) space))) return -__LINE__; + for (l=c=i=0; i < sl->qty; i++) { + if (sl->entry[i]->slen > 0) { + c ++; + l += sl->entry[i]->slen; + } + } + + if (l + c >= width || c < 2) { + bstrListDestroy (sl); + return bJustifyLeft (b, space); + } + + b->slen = 0; + for (i=0; i < sl->qty; i++) { + if (sl->entry[i]->slen > 0) { + if (b->slen > 0) { + int s = (width - l + (c / 2)) / c; + bInsertChrs (b, b->slen, s, (unsigned char) space, (unsigned char) space); + l += s; + } + bconcat (b, sl->entry[i]); + c--; + if (c <= 0) break; + } + } + + bstrListDestroy (sl); + return BSTR_OK; +} + +static size_t readNothing (void *buff, size_t elsize, size_t nelem, void *parm) { + buff = buff; + elsize = elsize; + nelem = nelem; + parm = parm; + return 0; /* Immediately indicate EOF. */ +} + +/* struct bStream * bsFromBstr (const_bstring b); + * + * Create a bStream whose contents are a copy of the bstring passed in. + * This allows the use of all the bStream APIs with bstrings. + */ +struct bStream * bsFromBstr (const_bstring b) { +struct bStream * s = bsopen ((bNread) readNothing, NULL); + bsunread (s, b); /* Push the bstring data into the empty bStream. */ + return s; +} + +static size_t readRef (void *buff, size_t elsize, size_t nelem, void *parm) { +struct tagbstring * t = (struct tagbstring *) parm; +size_t tsz = elsize * nelem; + + if (tsz > (size_t) t->slen) tsz = (size_t) t->slen; + if (tsz > 0) { + memcpy (buff, t->data, tsz); + t->slen -= (int) tsz; + t->data += tsz; + return tsz / elsize; + } + return 0; +} + +/* The "by reference" version of the above function. This function puts + * a number of restrictions on the call site (the passed in struct + * tagbstring *will* be modified by this function, and the source data + * must remain alive and constant for the lifetime of the bStream). + * Hence it is not presented as an extern. + */ +static struct bStream * bsFromBstrRef (struct tagbstring * t) { + if (!t) return NULL; + return bsopen ((bNread) readRef, t); +} + +/* char * bStr2NetStr (const_bstring b) + * + * Convert a bstring to a netstring. See + * http://cr.yp.to/proto/netstrings.txt for a description of netstrings. + * Note: 1) The value returned should be freed with a call to bcstrfree() at + * the point when it will no longer be referenced to avoid a memory + * leak. + * 2) If the returned value is non-NULL, then it also '\0' terminated + * in the character position one past the "," terminator. + */ +char * bStr2NetStr (const_bstring b) { +char strnum[sizeof (b->slen) * 3 + 1]; +bstring s; +unsigned char * buff; + + if (b == NULL || b->data == NULL || b->slen < 0) return NULL; + sprintf (strnum, "%d:", b->slen); + if (NULL == (s = bfromcstr (strnum)) + || bconcat (s, b) == BSTR_ERR || bconchar (s, (char) ',') == BSTR_ERR) { + bdestroy (s); + return NULL; + } + buff = s->data; + bcstrfree ((char *) s); + return (char *) buff; +} + +/* bstring bNetStr2Bstr (const char * buf) + * + * Convert a netstring to a bstring. See + * http://cr.yp.to/proto/netstrings.txt for a description of netstrings. + * Note that the terminating "," *must* be present, however a following '\0' + * is *not* required. + */ +bstring bNetStr2Bstr (const char * buff) { +int i, x; +bstring b; + if (buff == NULL) return NULL; + x = 0; + for (i=0; buff[i] != ':'; i++) { + unsigned int v = buff[i] - '0'; + if (v > 9 || x > ((INT_MAX - (signed int)v) / 10)) return NULL; + x = (x * 10) + v; + } + + /* This thing has to be properly terminated */ + if (buff[i + 1 + x] != ',') return NULL; + + if (NULL == (b = bfromcstr (""))) return NULL; + if (balloc (b, x + 1) != BSTR_OK) { + bdestroy (b); + return NULL; + } + memcpy (b->data, buff + i + 1, x); + b->data[x] = (unsigned char) '\0'; + b->slen = x; + return b; +} + +static char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* bstring bBase64Encode (const_bstring b) + * + * Generate a base64 encoding. See: RFC1341 + */ +bstring bBase64Encode (const_bstring b) { +int i, c0, c1, c2, c3; +bstring out; + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + + out = bfromcstr (""); + for (i=0; i + 2 < b->slen; i += 3) { + if (i && ((i % 57) == 0)) { + if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) { + bdestroy (out); + return NULL; + } + } + c0 = b->data[i] >> 2; + c1 = ((b->data[i] << 4) | + (b->data[i+1] >> 4)) & 0x3F; + c2 = ((b->data[i+1] << 2) | + (b->data[i+2] >> 6)) & 0x3F; + c3 = b->data[i+2] & 0x3F; + if (bconchar (out, b64ETable[c0]) < 0 || + bconchar (out, b64ETable[c1]) < 0 || + bconchar (out, b64ETable[c2]) < 0 || + bconchar (out, b64ETable[c3]) < 0) { + bdestroy (out); + return NULL; + } + } + + if (i && ((i % 57) == 0)) { + if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) { + bdestroy (out); + return NULL; + } + } + + switch (i + 2 - b->slen) { + case 0: c0 = b->data[i] >> 2; + c1 = ((b->data[i] << 4) | + (b->data[i+1] >> 4)) & 0x3F; + c2 = (b->data[i+1] << 2) & 0x3F; + if (bconchar (out, b64ETable[c0]) < 0 || + bconchar (out, b64ETable[c1]) < 0 || + bconchar (out, b64ETable[c2]) < 0 || + bconchar (out, (char) '=') < 0) { + bdestroy (out); + return NULL; + } + break; + case 1: c0 = b->data[i] >> 2; + c1 = (b->data[i] << 4) & 0x3F; + if (bconchar (out, b64ETable[c0]) < 0 || + bconchar (out, b64ETable[c1]) < 0 || + bconchar (out, (char) '=') < 0 || + bconchar (out, (char) '=') < 0) { + bdestroy (out); + return NULL; + } + break; + case 2: break; + } + + return out; +} + +#define B64_PAD (-2) +#define B64_ERR (-1) + +static int base64DecodeSymbol (unsigned char alpha) { + if ((alpha >= 'A') && (alpha <= 'Z')) return (int)(alpha - 'A'); + else if ((alpha >= 'a') && (alpha <= 'z')) + return 26 + (int)(alpha - 'a'); + else if ((alpha >= '0') && (alpha <= '9')) + return 52 + (int)(alpha - '0'); + else if (alpha == '+') return 62; + else if (alpha == '/') return 63; + else if (alpha == '=') return B64_PAD; + else return B64_ERR; +} + +/* bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) + * + * Decode a base64 block of data. All MIME headers are assumed to have been + * removed. See: RFC1341 + */ +bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) { +int i, v; +unsigned char c0, c1, c2; +bstring out; + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + if (boolTruncError) *boolTruncError = 0; + out = bfromcstr (""); + i = 0; + for (;;) { + do { + if (i >= b->slen) return out; + if (b->data[i] == '=') { /* Bad "too early" truncation */ + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c0 = (unsigned char) (v << 2); + do { + if (i >= b->slen || b->data[i] == '=') { /* Bad "too early" truncation */ + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c0 |= (unsigned char) (v >> 4); + c1 = (unsigned char) (v << 4); + do { + if (i >= b->slen) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + if (b->data[i] == '=') { + i++; + if (i >= b->slen || b->data[i] != '=' || bconchar (out, c0) < 0) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); /* Missing "=" at the end. */ + return NULL; + } + return out; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c1 |= (unsigned char) (v >> 2); + c2 = (unsigned char) (v << 6); + do { + if (i >= b->slen) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + if (b->data[i] == '=') { + if (bconchar (out, c0) < 0 || bconchar (out, c1) < 0) { + if (boolTruncError) { + *boolTruncError = 1; + return out; + } + bdestroy (out); + return NULL; + } + if (boolTruncError) *boolTruncError = 0; + return out; + } + v = base64DecodeSymbol (b->data[i]); + i++; + } while (v < 0); + c2 |= (unsigned char) (v); + if (bconchar (out, c0) < 0 || + bconchar (out, c1) < 0 || + bconchar (out, c2) < 0) { + if (boolTruncError) { + *boolTruncError = -1; + return out; + } + bdestroy (out); + return NULL; + } + } +} + +#define UU_DECODE_BYTE(b) (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ') + +struct bUuInOut { + bstring src, dst; + int * badlines; +}; + +#define UU_MAX_LINELEN 45 + +static int bUuDecLine (void * parm, int ofs, int len) { +struct bUuInOut * io = (struct bUuInOut *) parm; +bstring s = io->src; +bstring t = io->dst; +int i, llen, otlen, ret, c0, c1, c2, c3, d0, d1, d2, d3; + + if (len == 0) return 0; + llen = UU_DECODE_BYTE (s->data[ofs]); + ret = 0; + + otlen = t->slen; + + if (((unsigned) llen) > UU_MAX_LINELEN) { ret = -__LINE__; + goto bl; + } + + llen += t->slen; + + for (i=1; i < s->slen && t->slen < llen;i += 4) { + unsigned char outoctet[3]; + c0 = UU_DECODE_BYTE (d0 = (int) bchare (s, i+ofs+0, ' ' - 1)); + c1 = UU_DECODE_BYTE (d1 = (int) bchare (s, i+ofs+1, ' ' - 1)); + c2 = UU_DECODE_BYTE (d2 = (int) bchare (s, i+ofs+2, ' ' - 1)); + c3 = UU_DECODE_BYTE (d3 = (int) bchare (s, i+ofs+3, ' ' - 1)); + + if (((unsigned) (c0|c1) >= 0x40)) { if (!ret) ret = -__LINE__; + if (d0 > 0x60 || (d0 < (' ' - 1) && !isspace (d0)) || + d1 > 0x60 || (d1 < (' ' - 1) && !isspace (d1))) { + t->slen = otlen; + goto bl; + } + c0 = c1 = 0; + } + outoctet[0] = (unsigned char) ((c0 << 2) | ((unsigned) c1 >> 4)); + if (t->slen+1 >= llen) { + if (0 > bconchar (t, (char) outoctet[0])) return -__LINE__; + break; + } + if ((unsigned) c2 >= 0x40) { if (!ret) ret = -__LINE__; + if (d2 > 0x60 || (d2 < (' ' - 1) && !isspace (d2))) { + t->slen = otlen; + goto bl; + } + c2 = 0; + } + outoctet[1] = (unsigned char) ((c1 << 4) | ((unsigned) c2 >> 2)); + if (t->slen+2 >= llen) { + if (0 > bcatblk (t, outoctet, 2)) return -__LINE__; + break; + } + if ((unsigned) c3 >= 0x40) { if (!ret) ret = -__LINE__; + if (d3 > 0x60 || (d3 < (' ' - 1) && !isspace (d3))) { + t->slen = otlen; + goto bl; + } + c3 = 0; + } + outoctet[2] = (unsigned char) ((c2 << 6) | ((unsigned) c3)); + if (0 > bcatblk (t, outoctet, 3)) return -__LINE__; + } + if (t->slen < llen) { if (0 == ret) ret = -__LINE__; + t->slen = otlen; + } + bl:; + if (ret && io->badlines) { + (*io->badlines)++; + return 0; + } + return ret; +} + +/* bstring bUuDecodeEx (const_bstring src, int * badlines) + * + * Performs a UUDecode of a block of data. If there are errors in the + * decoding, they are counted up and returned in "badlines", if badlines is + * not NULL. It is assumed that the "begin" and "end" lines have already + * been stripped off. The potential security problem of writing the + * filename in the begin line is something that is beyond the scope of a + * portable library. + */ + +#ifdef _MSC_VER +#pragma warning(disable:4204) +#endif + +bstring bUuDecodeEx (const_bstring src, int * badlines) { +struct tagbstring t; +struct bStream * s; +struct bStream * d; +bstring b; + + if (!src) return NULL; + t = *src; /* Short lifetime alias to header of src */ + s = bsFromBstrRef (&t); /* t is undefined after this */ + if (!s) return NULL; + d = bsUuDecode (s, badlines); + b = bfromcstralloc (256, ""); + if (NULL == b || 0 > bsread (b, d, INT_MAX)) { + bdestroy (b); + bsclose (d); + bsclose (s); + return NULL; + } + return b; +} + +struct bsUuCtx { + struct bUuInOut io; + struct bStream * sInp; +}; + +static size_t bsUuDecodePart (void *buff, size_t elsize, size_t nelem, void *parm) { +static struct tagbstring eol = bsStatic ("\r\n"); +struct bsUuCtx * luuCtx = (struct bsUuCtx *) parm; +size_t tsz; +int l, lret; + + if (NULL == buff || NULL == parm) return 0; + tsz = elsize * nelem; + + CheckInternalBuffer:; + /* If internal buffer has sufficient data, just output it */ + if (((size_t) luuCtx->io.dst->slen) > tsz) { + memcpy (buff, luuCtx->io.dst->data, tsz); + bdelete (luuCtx->io.dst, 0, (int) tsz); + return nelem; + } + + DecodeMore:; + if (0 <= (l = binchr (luuCtx->io.src, 0, &eol))) { + int ol = 0; + struct tagbstring t; + bstring s = luuCtx->io.src; + luuCtx->io.src = &t; + + do { + if (l > ol) { + bmid2tbstr (t, s, ol, l - ol); + lret = bUuDecLine (&luuCtx->io, 0, t.slen); + if (0 > lret) { + luuCtx->io.src = s; + goto Done; + } + } + ol = l + 1; + if (((size_t) luuCtx->io.dst->slen) > tsz) break; + l = binchr (s, ol, &eol); + } while (BSTR_ERR != l); + bdelete (s, 0, ol); + luuCtx->io.src = s; + goto CheckInternalBuffer; + } + + if (BSTR_ERR != bsreada (luuCtx->io.src, luuCtx->sInp, bsbufflength (luuCtx->sInp, BSTR_BS_BUFF_LENGTH_GET))) { + goto DecodeMore; + } + + bUuDecLine (&luuCtx->io, 0, luuCtx->io.src->slen); + + Done:; + /* Output any lingering data that has been translated */ + if (((size_t) luuCtx->io.dst->slen) > 0) { + if (((size_t) luuCtx->io.dst->slen) > tsz) goto CheckInternalBuffer; + memcpy (buff, luuCtx->io.dst->data, luuCtx->io.dst->slen); + tsz = luuCtx->io.dst->slen / elsize; + luuCtx->io.dst->slen = 0; + if (tsz > 0) return tsz; + } + + /* Deallocate once EOF becomes triggered */ + bdestroy (luuCtx->io.dst); + bdestroy (luuCtx->io.src); + free (luuCtx); + return 0; +} + +/* bStream * bsUuDecode (struct bStream * sInp, int * badlines) + * + * Creates a bStream which performs the UUDecode of an an input stream. If + * there are errors in the decoding, they are counted up and returned in + * "badlines", if badlines is not NULL. It is assumed that the "begin" and + * "end" lines have already been stripped off. The potential security + * problem of writing the filename in the begin line is something that is + * beyond the scope of a portable library. + */ + +struct bStream * bsUuDecode (struct bStream * sInp, int * badlines) { +struct bsUuCtx * luuCtx = (struct bsUuCtx *) malloc (sizeof (struct bsUuCtx)); +struct bStream * sOut; + + if (NULL == luuCtx) return NULL; + + luuCtx->io.src = bfromcstr (""); + luuCtx->io.dst = bfromcstr (""); + if (NULL == luuCtx->io.dst || NULL == luuCtx->io.src) { + CleanUpFailureToAllocate:; + bdestroy (luuCtx->io.dst); + bdestroy (luuCtx->io.src); + free (luuCtx); + return NULL; + } + luuCtx->io.badlines = badlines; + if (badlines) *badlines = 0; + + luuCtx->sInp = sInp; + + sOut = bsopen ((bNread) bsUuDecodePart, luuCtx); + if (NULL == sOut) goto CleanUpFailureToAllocate; + return sOut; +} + +#define UU_ENCODE_BYTE(b) (char) (((b) == 0) ? '`' : ((b) + ' ')) + +/* bstring bUuEncode (const_bstring src) + * + * Performs a UUEncode of a block of data. The "begin" and "end" lines are + * not appended. + */ +bstring bUuEncode (const_bstring src) { +bstring out; +int i, j, jm; +unsigned int c0, c1, c2; + if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; + if ((out = bfromcstr ("")) == NULL) return NULL; + for (i=0; i < src->slen; i += UU_MAX_LINELEN) { + if ((jm = i + UU_MAX_LINELEN) > src->slen) jm = src->slen; + if (bconchar (out, UU_ENCODE_BYTE (jm - i)) < 0) { + bstrFree (out); + break; + } + for (j = i; j < jm; j += 3) { + c0 = (unsigned int) bchar (src, j ); + c1 = (unsigned int) bchar (src, j + 1); + c2 = (unsigned int) bchar (src, j + 2); + if (bconchar (out, UU_ENCODE_BYTE ( (c0 & 0xFC) >> 2)) < 0 || + bconchar (out, UU_ENCODE_BYTE (((c0 & 0x03) << 4) | ((c1 & 0xF0) >> 4))) < 0 || + bconchar (out, UU_ENCODE_BYTE (((c1 & 0x0F) << 2) | ((c2 & 0xC0) >> 6))) < 0 || + bconchar (out, UU_ENCODE_BYTE ( (c2 & 0x3F))) < 0) { + bstrFree (out); + goto End; + } + } + if (bconchar (out, (char) '\r') < 0 || bconchar (out, (char) '\n') < 0) { + bstrFree (out); + break; + } + } + End:; + return out; +} + +/* bstring bYEncode (const_bstring src) + * + * Performs a YEncode of a block of data. No header or tail info is + * appended. See: http://www.yenc.org/whatis.htm and + * http://www.yenc.org/yenc-draft.1.3.txt + */ +bstring bYEncode (const_bstring src) { +int i; +bstring out; +unsigned char c; + + if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; + if ((out = bfromcstr ("")) == NULL) return NULL; + for (i=0; i < src->slen; i++) { + c = (unsigned char)(src->data[i] + 42); + if (c == '=' || c == '\0' || c == '\r' || c == '\n') { + if (0 > bconchar (out, (char) '=')) { + bdestroy (out); + return NULL; + } + c += (unsigned char) 64; + } + if (0 > bconchar (out, c)) { + bdestroy (out); + return NULL; + } + } + return out; +} + +/* bstring bYDecode (const_bstring src) + * + * Performs a YDecode of a block of data. See: + * http://www.yenc.org/whatis.htm and http://www.yenc.org/yenc-draft.1.3.txt + */ +#define MAX_OB_LEN (64) + +bstring bYDecode (const_bstring src) { +int i; +bstring out; +unsigned char c; +unsigned char octetbuff[MAX_OB_LEN]; +int obl; + + if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; + if ((out = bfromcstr ("")) == NULL) return NULL; + + obl = 0; + + for (i=0; i < src->slen; i++) { + if ('=' == (c = src->data[i])) { /* The = escape mode */ + i++; + if (i >= src->slen) { + bdestroy (out); + return NULL; + } + c = (unsigned char) (src->data[i] - 64); + } else { + if ('\0' == c) { + bdestroy (out); + return NULL; + } + + /* Extraneous CR/LFs are to be ignored. */ + if (c == '\r' || c == '\n') continue; + } + + octetbuff[obl] = (unsigned char) ((int) c - 42); + obl++; + + if (obl >= MAX_OB_LEN) { + if (0 > bcatblk (out, octetbuff, obl)) { + bdestroy (out); + return NULL; + } + obl = 0; + } + } + + if (0 > bcatblk (out, octetbuff, obl)) { + bdestroy (out); + out = NULL; + } + return out; +} + +/* bstring bStrfTime (const char * fmt, const struct tm * timeptr) + * + * Takes a format string that is compatible with strftime and a struct tm + * pointer, formats the time according to the format string and outputs + * the bstring as a result. Note that if there is an early generation of a + * '\0' character, the bstring will be truncated to this end point. + */ +bstring bStrfTime (const char * fmt, const struct tm * timeptr) { +#if defined (__TURBOC__) && !defined (__BORLANDC__) +static struct tagbstring ns = bsStatic ("bStrfTime Not supported"); + fmt = fmt; + timeptr = timeptr; + return &ns; +#else +bstring buff; +int n; +size_t r; + + if (fmt == NULL) return NULL; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "strftime" call on increasing + potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < 16) n = 16; + buff = bfromcstralloc (n+2, ""); + + for (;;) { + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return NULL; + } + + r = strftime ((char *) buff->data, n + 1, fmt, timeptr); + + if (r > 0) { + buff->slen = (int) r; + break; + } + + n += n; + } + + return buff; +#endif +} + +/* int bSetCstrChar (bstring a, int pos, char c) + * + * Sets the character at position pos to the character c in the bstring a. + * If the character c is NUL ('\0') then the string is truncated at this + * point. Note: this does not enable any other '\0' character in the bstring + * as terminator indicator for the string. pos must be in the position + * between 0 and b->slen inclusive, otherwise BSTR_ERR will be returned. + */ +int bSetCstrChar (bstring b, int pos, char c) { + if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) + return BSTR_ERR; + if (pos < 0 || pos > b->slen) return BSTR_ERR; + + if (pos == b->slen) { + if ('\0' != c) return bconchar (b, c); + return 0; + } + + b->data[pos] = (unsigned char) c; + if ('\0' == c) b->slen = pos; + + return 0; +} + +/* int bSetChar (bstring b, int pos, char c) + * + * Sets the character at position pos to the character c in the bstring a. + * The string is not truncated if the character c is NUL ('\0'). pos must + * be in the position between 0 and b->slen inclusive, otherwise BSTR_ERR + * will be returned. + */ +int bSetChar (bstring b, int pos, char c) { + if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) + return BSTR_ERR; + if (pos < 0 || pos > b->slen) return BSTR_ERR; + + if (pos == b->slen) { + return bconchar (b, c); + } + + b->data[pos] = (unsigned char) c; + return 0; +} + +#define INIT_SECURE_INPUT_LENGTH (256) + +/* bstring bSecureInput (int maxlen, int termchar, + * bNgetc vgetchar, void * vgcCtx) + * + * Read input from an abstracted input interface, for a length of at most + * maxlen characters. If maxlen <= 0, then there is no length limit put + * on the input. The result is terminated early if vgetchar() return EOF + * or the user specified value termchar. + * + */ +bstring bSecureInput (int maxlen, int termchar, bNgetc vgetchar, void * vgcCtx) { +int i, m, c; +bstring b, t; + + if (!vgetchar) return NULL; + + b = bfromcstralloc (INIT_SECURE_INPUT_LENGTH, ""); + if ((c = UCHAR_MAX + 1) == termchar) c++; + + for (i=0; ; i++) { + if (termchar == c || (maxlen > 0 && i >= maxlen)) c = EOF; + else c = vgetchar (vgcCtx); + + if (EOF == c) break; + + if (i+1 >= b->mlen) { + + /* Double size, but deal with unusual case of numeric + overflows */ + + if ((m = b->mlen << 1) <= b->mlen && + (m = b->mlen + 1024) <= b->mlen && + (m = b->mlen + 16) <= b->mlen && + (m = b->mlen + 1) <= b->mlen) t = NULL; + else t = bfromcstralloc (m, ""); + + if (t) memcpy (t->data, b->data, i); + bSecureDestroy (b); /* Cleanse previous buffer */ + b = t; + if (!b) return b; + } + + b->data[i] = (unsigned char) c; + } + + b->slen = i; + b->data[i] = (unsigned char) '\0'; + return b; +} + +#define BWS_BUFF_SZ (1024) + +struct bwriteStream { + bstring buff; /* Buffer for underwrites */ + void * parm; /* The stream handle for core stream */ + bNwrite writeFn; /* fwrite work-a-like fnptr for core stream */ + int isEOF; /* track stream's EOF state */ + int minBuffSz; +}; + +/* struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) + * + * Wrap a given open stream (described by a fwrite work-a-like function + * pointer and stream handle) into an open bwriteStream suitable for write + * streaming functions. + */ +struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) { +struct bwriteStream * ws; + + if (NULL == writeFn) return NULL; + ws = (struct bwriteStream *) malloc (sizeof (struct bwriteStream)); + if (ws) { + if (NULL == (ws->buff = bfromcstr (""))) { + free (ws); + ws = NULL; + } else { + ws->parm = parm; + ws->writeFn = writeFn; + ws->isEOF = 0; + ws->minBuffSz = BWS_BUFF_SZ; + } + } + return ws; +} + +#define internal_bwswriteout(ws,b) { \ + if ((b)->slen > 0) { \ + if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \ + ws->isEOF = 1; \ + return BSTR_ERR; \ + } \ + } \ +} + +/* int bwsWriteFlush (struct bwriteStream * ws) + * + * Force any pending data to be written to the core stream. + */ +int bwsWriteFlush (struct bwriteStream * ws) { + if (NULL == ws || ws->isEOF || 0 >= ws->minBuffSz || + NULL == ws->writeFn || NULL == ws->buff) return BSTR_ERR; + internal_bwswriteout (ws, ws->buff); + ws->buff->slen = 0; + return 0; +} + +/* int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) + * + * Send a bstring to a bwriteStream. If the stream is at EOF BSTR_ERR is + * returned. Note that there is no deterministic way to determine the exact + * cut off point where the core stream stopped accepting data. + */ +int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) { +struct tagbstring t; +int l; + + if (NULL == ws || NULL == b || NULL == ws->buff || + ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn) + return BSTR_ERR; + + /* Buffer prepacking optimization */ + if (b->slen > 0 && ws->buff->mlen - ws->buff->slen > b->slen) { + static struct tagbstring empty = bsStatic (""); + if (0 > bconcat (ws->buff, b)) return BSTR_ERR; + return bwsWriteBstr (ws, &empty); + } + + if (0 > (l = ws->minBuffSz - ws->buff->slen)) { + internal_bwswriteout (ws, ws->buff); + ws->buff->slen = 0; + l = ws->minBuffSz; + } + + if (b->slen < l) return bconcat (ws->buff, b); + + if (0 > bcatblk (ws->buff, b->data, l)) return BSTR_ERR; + internal_bwswriteout (ws, ws->buff); + ws->buff->slen = 0; + + bmid2tbstr (t, (bstring) b, l, b->slen); + + if (t.slen >= ws->minBuffSz) { + internal_bwswriteout (ws, &t); + return 0; + } + + return bassign (ws->buff, &t); +} + +/* int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) + * + * Send a block of data a bwriteStream. If the stream is at EOF BSTR_ERR is + * returned. + */ +int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) { +struct tagbstring t; + if (NULL == blk || len < 0) return BSTR_ERR; + blk2tbstr (t, blk, len); + return bwsWriteBstr (ws, &t); +} + +/* int bwsIsEOF (const struct bwriteStream * ws) + * + * Returns 0 if the stream is currently writable, 1 if the core stream has + * responded by not accepting the previous attempted write. + */ +int bwsIsEOF (const struct bwriteStream * ws) { + if (NULL == ws || NULL == ws->buff || 0 > ws->minBuffSz || + NULL == ws->writeFn) return BSTR_ERR; + return ws->isEOF; +} + +/* int bwsBuffLength (struct bwriteStream * ws, int sz) + * + * Set the length of the buffer used by the bwsStream. If sz is zero, the + * length is not set. This function returns with the previous length. + */ +int bwsBuffLength (struct bwriteStream * ws, int sz) { +int oldSz; + if (ws == NULL || sz < 0) return BSTR_ERR; + oldSz = ws->minBuffSz; + if (sz > 0) ws->minBuffSz = sz; + return oldSz; +} + +/* void * bwsClose (struct bwriteStream * s) + * + * Close the bwriteStream, and return the handle to the stream that was + * originally used to open the given stream. Note that even if the stream + * is at EOF it still needs to be closed with a call to bwsClose. + */ +void * bwsClose (struct bwriteStream * ws) { +void * parm; + if (NULL == ws || NULL == ws->buff || 0 >= ws->minBuffSz || + NULL == ws->writeFn) return NULL; + bwsWriteFlush (ws); + parm = ws->parm; + ws->parm = NULL; + ws->minBuffSz = -1; + ws->writeFn = NULL; + bstrFree (ws->buff); + free (ws); + return parm; +} + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.h b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.h new file mode 100644 index 0000000000..e10c6e1a68 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstraux.h @@ -0,0 +1,113 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstraux.h + * + * This file is not a necessary part of the core bstring library itself, but + * is just an auxilliary module which includes miscellaneous or trivial + * functions. + */ + +#ifndef BSTRAUX_INCLUDE +#define BSTRAUX_INCLUDE + +#include +#include "bstrlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Safety mechanisms */ +#define bstrDeclare(b) bstring (b) = NULL; +#define bstrFree(b) {if ((b) != NULL && (b)->slen >= 0 && (b)->mlen >= (b)->slen) { bdestroy (b); (b) = NULL; }} + +/* Backward compatibilty with previous versions of Bstrlib */ +#define bAssign(a,b) ((bassign)((a), (b))) +#define bSubs(b,pos,len,a,c) ((breplace)((b),(pos),(len),(a),(unsigned char)(c))) +#define bStrchr(b,c) ((bstrchr)((b), (c))) +#define bStrchrFast(b,c) ((bstrchr)((b), (c))) +#define bCatCstr(b,s) ((bcatcstr)((b), (s))) +#define bCatBlk(b,s,len) ((bcatblk)((b),(s),(len))) +#define bCatStatic(b,s) bCatBlk ((b), ("" s ""), sizeof (s) - 1) +#define bTrunc(b,n) ((btrunc)((b), (n))) +#define bReplaceAll(b,find,repl,pos) ((bfindreplace)((b),(find),(repl),(pos))) +#define bUppercase(b) ((btoupper)(b)) +#define bLowercase(b) ((btolower)(b)) +#define bCaselessCmp(a,b) ((bstricmp)((a), (b))) +#define bCaselessNCmp(a,b,n) ((bstrnicmp)((a), (b), (n))) +#define bBase64Decode(b) (bBase64DecodeEx ((b), NULL)) +#define bUuDecode(b) (bUuDecodeEx ((b), NULL)) + +/* Unusual functions */ +extern struct bStream * bsFromBstr (const_bstring b); +extern bstring bTail (bstring b, int n); +extern bstring bHead (bstring b, int n); +extern int bSetCstrChar (bstring a, int pos, char c); +extern int bSetChar (bstring b, int pos, char c); +extern int bFill (bstring a, char c, int len); +extern int bReplicate (bstring b, int n); +extern int bReverse (bstring b); +extern int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill); +extern bstring bStrfTime (const char * fmt, const struct tm * timeptr); +#define bAscTime(t) (bStrfTime ("%c\n", (t))) +#define bCTime(t) ((t) ? bAscTime (localtime (t)) : NULL) + +/* Spacing formatting */ +extern int bJustifyLeft (bstring b, int space); +extern int bJustifyRight (bstring b, int width, int space); +extern int bJustifyMargin (bstring b, int width, int space); +extern int bJustifyCenter (bstring b, int width, int space); + +/* Esoteric standards specific functions */ +extern char * bStr2NetStr (const_bstring b); +extern bstring bNetStr2Bstr (const char * buf); +extern bstring bBase64Encode (const_bstring b); +extern bstring bBase64DecodeEx (const_bstring b, int * boolTruncError); +extern struct bStream * bsUuDecode (struct bStream * sInp, int * badlines); +extern bstring bUuDecodeEx (const_bstring src, int * badlines); +extern bstring bUuEncode (const_bstring src); +extern bstring bYEncode (const_bstring src); +extern bstring bYDecode (const_bstring src); + +/* Writable stream */ +typedef int (* bNwrite) (const void * buf, size_t elsize, size_t nelem, void * parm); + +struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm); +int bwsWriteBstr (struct bwriteStream * stream, const_bstring b); +int bwsWriteBlk (struct bwriteStream * stream, void * blk, int len); +int bwsWriteFlush (struct bwriteStream * stream); +int bwsIsEOF (const struct bwriteStream * stream); +int bwsBuffLength (struct bwriteStream * stream, int sz); +void * bwsClose (struct bwriteStream * stream); + +/* Security functions */ +#define bSecureDestroy(b) { \ +bstring bstr__tmp = (b); \ + if (bstr__tmp && bstr__tmp->mlen > 0 && bstr__tmp->data) { \ + (void) memset (bstr__tmp->data, 0, (size_t) bstr__tmp->mlen); \ + bdestroy (bstr__tmp); \ + } \ +} +#define bSecureWriteProtect(t) { \ + if ((t).mlen >= 0) { \ + if ((t).mlen > (t).slen)) { \ + (void) memset ((t).data + (t).slen, 0, (size_t) (t).mlen - (t).slen); \ + } \ + (t).mlen = -1; \ + } \ +} +extern bstring bSecureInput (int maxlen, int termchar, + bNgetc vgetchar, void * vgcCtx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.c b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.c new file mode 100644 index 0000000000..61c8c60ee1 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.c @@ -0,0 +1,2976 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstrlib.c + * + * This file is the core module for implementing the bstring functions. + */ + +#include +#include +#include +#include +#include +#include +#include "bstrlib.h" +#include "../internal_includes/hlslcc_malloc.h" + +/* Optionally include a mechanism for debugging memory */ + +#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG) +#include "memdbg.h" +#endif + +#ifndef bstr__alloc +#define bstr__alloc(x) malloc (x) +#endif + +#ifndef bstr__free +#define bstr__free(p) free (p) +#endif + +#ifndef bstr__realloc +#define bstr__realloc(p,x) realloc ((p), (x)) +#endif + +#ifndef bstr__memcpy +#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l)) +#endif + +#ifndef bstr__memmove +#define bstr__memmove(d,s,l) memmove ((d), (s), (l)) +#endif + +#ifndef bstr__memset +#define bstr__memset(d,c,l) memset ((d), (c), (l)) +#endif + +#ifndef bstr__memcmp +#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l)) +#endif + +#ifndef bstr__memchr +#define bstr__memchr(s,c,l) memchr ((s), (c), (l)) +#endif + +/* Just a length safe wrapper for memmove. */ + +#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); } + +/* Compute the snapped size for a given requested size. By snapping to powers + of 2 like this, repeated reallocations are avoided. */ +static int snapUpSize (int i) { + if (i < 8) { + i = 8; + } else { + unsigned int j; + j = (unsigned int) i; + + j |= (j >> 1); + j |= (j >> 2); + j |= (j >> 4); + j |= (j >> 8); /* Ok, since int >= 16 bits */ +#if (UINT_MAX != 0xffff) + j |= (j >> 16); /* For 32 bit int systems */ +#if (UINT_MAX > 0xffffffffUL) + j |= (j >> 32); /* For 64 bit int systems */ +#endif +#endif + /* Least power of two greater than i */ + j++; + if ((int) j >= i) i = (int) j; + } + return i; +} + +/* int balloc (bstring b, int len) + * + * Increase the size of the memory backing the bstring b to at least len. + */ +int balloc (bstring b, int olen) { + int len; + if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || + b->mlen < b->slen || olen <= 0) { + return BSTR_ERR; + } + + if (olen >= b->mlen) { + unsigned char * x; + + if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK; + + /* Assume probability of a non-moving realloc is 0.125 */ + if (7 * b->mlen < 8 * b->slen) { + + /* If slen is close to mlen in size then use realloc to reduce + the memory defragmentation */ + + reallocStrategy:; + + x = (unsigned char *) bstr__realloc (b->data, (size_t) len); + if (x == NULL) { + + /* Since we failed, try allocating the tighest possible + allocation */ + + if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) { + return BSTR_ERR; + } + } + } else { + + /* If slen is not close to mlen then avoid the penalty of copying + the extra bytes that are allocated, but not considered part of + the string */ + + if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) { + + /* Perhaps there is no available memory for the two + allocations to be in memory at once */ + + goto reallocStrategy; + + } else { + if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen); + bstr__free (b->data); + } + } + b->data = x; + b->mlen = len; + b->data[b->slen] = (unsigned char) '\0'; + } + + return BSTR_OK; +} + +/* int ballocmin (bstring b, int len) + * + * Set the size of the memory backing the bstring b to len or b->slen+1, + * whichever is larger. Note that repeated use of this function can degrade + * performance. + */ +int ballocmin (bstring b, int len) { + unsigned char * s; + + if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 || + b->mlen < b->slen || len <= 0) { + return BSTR_ERR; + } + + if (len < b->slen + 1) len = b->slen + 1; + + if (len != b->mlen) { + s = (unsigned char *) bstr__realloc (b->data, (size_t) len); + if (NULL == s) return BSTR_ERR; + s[b->slen] = (unsigned char) '\0'; + b->data = s; + b->mlen = len; + } + + return BSTR_OK; +} + +/* bstring bfromcstr (const char * str) + * + * Create a bstring which contains the contents of the '\0' terminated char * + * buffer str. + */ +bstring bfromcstr (const char * str) { +bstring b; +int i; +size_t j; + + if (str == NULL) return NULL; + j = (strlen) (str); + i = snapUpSize ((int) (j + (2 - (j != 0)))); + if (i <= (int) j) return NULL; + + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (NULL == b) return NULL; + b->slen = (int) j; + if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) { + bstr__free (b); + return NULL; + } + + bstr__memcpy (b->data, str, j+1); + return b; +} + +/* bstring bfromcstralloc (int mlen, const char * str) + * + * Create a bstring which contains the contents of the '\0' terminated char * + * buffer str. The memory buffer backing the string is at least len + * characters in length. + */ +bstring bfromcstralloc (int mlen, const char * str) { +bstring b; +int i; +size_t j; + + if (str == NULL) return NULL; + j = (strlen) (str); + i = snapUpSize ((int) (j + (2 - (j != 0)))); + if (i <= (int) j) return NULL; + + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (b == NULL) return NULL; + b->slen = (int) j; + if (i < mlen) i = mlen; + + if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) { + bstr__free (b); + return NULL; + } + + bstr__memcpy (b->data, str, j+1); + return b; +} + +/* bstring blk2bstr (const void * blk, int len) + * + * Create a bstring which contains the content of the block blk of length + * len. + */ +bstring blk2bstr (const void * blk, int len) { +bstring b; +int i; + + if (blk == NULL || len < 0) return NULL; + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (b == NULL) return NULL; + b->slen = len; + + i = len + (2 - (len != 0)); + i = snapUpSize (i); + + b->mlen = i; + + b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen); + if (b->data == NULL) { + bstr__free (b); + return NULL; + } + + if (len > 0) bstr__memcpy (b->data, blk, (size_t) len); + b->data[len] = (unsigned char) '\0'; + + return b; +} + +/* char * bstr2cstr (const_bstring s, char z) + * + * Create a '\0' terminated char * buffer which is equal to the contents of + * the bstring s, except that any contained '\0' characters are converted + * to the character in z. This returned value should be freed with a + * bcstrfree () call, by the calling application. + */ +char * bstr2cstr (const_bstring b, char z) { +int i, l; +char * r; + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + l = b->slen; + r = (char *) bstr__alloc ((size_t) (l + 1)); + if (r == NULL) return r; + + for (i=0; i < l; i ++) { + r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i])); + } + + r[l] = (unsigned char) '\0'; + + return r; +} + +/* int bcstrfree (char * s) + * + * Frees a C-string generated by bstr2cstr (). This is normally unnecessary + * since it just wraps a call to bstr__free (), however, if bstr__alloc () + * and bstr__free () have been redefined as a macros within the bstrlib + * module (via defining them in memdbg.h after defining + * BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std + * library functions, then this allows a correct way of freeing the memory + * that allows higher level code to be independent from these macro + * redefinitions. + */ +int bcstrfree (char * s) { + if (s) { + bstr__free (s); + return BSTR_OK; + } + return BSTR_ERR; +} + +/* int bconcat (bstring b0, const_bstring b1) + * + * Concatenate the bstring b1 to the bstring b0. + */ +int bconcat (bstring b0, const_bstring b1) { +int len, d; +bstring aux = (bstring) b1; + + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR; + + d = b0->slen; + len = b1->slen; + if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR; + + if (b0->mlen <= d + len + 1) { + ptrdiff_t pd = b1->data - b0->data; + if (0 <= pd && pd < b0->mlen) { + if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; + } + if (balloc (b0, d + len + 1) != BSTR_OK) { + if (aux != b1) bdestroy (aux); + return BSTR_ERR; + } + } + + bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len); + b0->data[d + len] = (unsigned char) '\0'; + b0->slen = d + len; + if (aux != b1) bdestroy (aux); + return BSTR_OK; +} + +/* int bconchar (bstring b, char c) +/ * + * Concatenate the single character c to the bstring b. + */ +int bconchar (bstring b, char c) { +int d; + + if (b == NULL) return BSTR_ERR; + d = b->slen; + if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; + b->data[d] = (unsigned char) c; + b->data[d + 1] = (unsigned char) '\0'; + b->slen++; + return BSTR_OK; +} + +/* int bcatcstr (bstring b, const char * s) + * + * Concatenate a char * string to a bstring. + */ +int bcatcstr (bstring b, const char * s) { +char * d; +int i, l; + + if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen + || b->mlen <= 0 || s == NULL) return BSTR_ERR; + + /* Optimistically concatenate directly */ + l = b->mlen - b->slen; + d = (char *) &b->data[b->slen]; + for (i=0; i < l; i++) { + if ((*d++ = *s++) == '\0') { + b->slen += i; + return BSTR_OK; + } + } + b->slen += i; + + /* Need to explicitely resize and concatenate tail */ + return bcatblk (b, (const void *) s, (int) strlen (s)); +} + +/* int bcatblk (bstring b, const void * s, int len) + * + * Concatenate a fixed length buffer to a bstring. + */ +int bcatblk (bstring b, const void * s, int len) { +int nl; + + if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen + || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR; + + if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */ + if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR; + + bBlockCopy (&b->data[b->slen], s, (size_t) len); + b->slen = nl; + b->data[nl] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* bstring bstrcpy (const_bstring b) + * + * Create a copy of the bstring b. + */ +bstring bstrcpy (const_bstring b) { +bstring b0; +int i,j; + + /* Attempted to copy an invalid string? */ + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + + b0 = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (b0 == NULL) { + /* Unable to allocate memory for string header */ + return NULL; + } + + i = b->slen; + j = snapUpSize (i + 1); + + b0->data = (unsigned char *) bstr__alloc (j); + if (b0->data == NULL) { + j = i + 1; + b0->data = (unsigned char *) bstr__alloc (j); + if (b0->data == NULL) { + /* Unable to allocate memory for string data */ + bstr__free (b0); + return NULL; + } + } + + b0->mlen = j; + b0->slen = i; + + if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i); + b0->data[b0->slen] = (unsigned char) '\0'; + + return b0; +} + +/* int bassign (bstring a, const_bstring b) + * + * Overwrite the string a with the contents of string b. + */ +int bassign (bstring a, const_bstring b) { + if (b == NULL || b->data == NULL || b->slen < 0) + return BSTR_ERR; + if (b->slen != 0) { + if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR; + bstr__memmove (a->data, b->data, b->slen); + } else { + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0) + return BSTR_ERR; + } + a->data[b->slen] = (unsigned char) '\0'; + a->slen = b->slen; + return BSTR_OK; +} + +/* int bassignmidstr (bstring a, const_bstring b, int left, int len) + * + * Overwrite the string a with the middle of contents of string b + * starting from position left and running for a length len. left and + * len are clamped to the ends of b as with the function bmidstr. + */ +int bassignmidstr (bstring a, const_bstring b, int left, int len) { + if (b == NULL || b->data == NULL || b->slen < 0) + return BSTR_ERR; + + if (left < 0) { + len += left; + left = 0; + } + + if (len > b->slen - left) len = b->slen - left; + + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0) + return BSTR_ERR; + + if (len > 0) { + if (balloc (a, len) != BSTR_OK) return BSTR_ERR; + bstr__memmove (a->data, b->data + left, len); + a->slen = len; + } else { + a->slen = 0; + } + a->data[a->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bassigncstr (bstring a, const char * str) + * + * Overwrite the string a with the contents of char * string str. Note that + * the bstring a must be a well defined and writable bstring. If an error + * occurs BSTR_ERR is returned however a may be partially overwritten. + */ +int bassigncstr (bstring a, const char * str) { +int i; +size_t len; + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0 || NULL == str) + return BSTR_ERR; + + for (i=0; i < a->mlen; i++) { + if ('\0' == (a->data[i] = str[i])) { + a->slen = i; + return BSTR_OK; + } + } + + a->slen = i; + len = strlen (str + i); + if (len > INT_MAX || i + len + 1 > INT_MAX || + 0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR; + bBlockCopy (a->data + i, str + i, (size_t) len + 1); + a->slen += (int) len; + return BSTR_OK; +} + +/* int bassignblk (bstring a, const void * s, int len) + * + * Overwrite the string a with the contents of the block (s, len). Note that + * the bstring a must be a well defined and writable bstring. If an error + * occurs BSTR_ERR is returned and a is not overwritten. + */ +int bassignblk (bstring a, const void * s, int len) { + if (a == NULL || a->data == NULL || a->mlen < a->slen || + a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1) + return BSTR_ERR; + if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR; + bBlockCopy (a->data, s, (size_t) len); + a->data[len] = (unsigned char) '\0'; + a->slen = len; + return BSTR_OK; +} + +/* int btrunc (bstring b, int n) + * + * Truncate the bstring to at most n characters. + */ +int btrunc (bstring b, int n) { + if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + if (b->slen > n) { + b->slen = n; + b->data[n] = (unsigned char) '\0'; + } + return BSTR_OK; +} + +#define upcase(c) (toupper ((unsigned char) c)) +#define downcase(c) (tolower ((unsigned char) c)) +#define wspace(c) (isspace ((unsigned char) c)) + +/* int btoupper (bstring b) + * + * Convert contents of bstring to upper case. + */ +int btoupper (bstring b) { +int i, len; + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + for (i=0, len = b->slen; i < len; i++) { + b->data[i] = (unsigned char) upcase (b->data[i]); + } + return BSTR_OK; +} + +/* int btolower (bstring b) + * + * Convert contents of bstring to lower case. + */ +int btolower (bstring b) { +int i, len; + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + for (i=0, len = b->slen; i < len; i++) { + b->data[i] = (unsigned char) downcase (b->data[i]); + } + return BSTR_OK; +} + +/* int bstricmp (const_bstring b0, const_bstring b1) + * + * Compare two strings without differentiating between case. The return + * value is the difference of the values of the characters where the two + * strings first differ after lower case transformation, otherwise 0 is + * returned indicating that the strings are equal. If the lengths are + * different, then a difference from 0 is given, but if the first extra + * character is '\0', then it is taken to be the value UCHAR_MAX+1. + */ +int bstricmp (const_bstring b0, const_bstring b1) { +int i, v, n; + + if (bdata (b0) == NULL || b0->slen < 0 || + bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN; + if ((n = b0->slen) > b1->slen) n = b1->slen; + else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK; + + for (i = 0; i < n; i ++) { + v = (char) downcase (b0->data[i]) + - (char) downcase (b1->data[i]); + if (0 != v) return v; + } + + if (b0->slen > n) { + v = (char) downcase (b0->data[n]); + if (v) return v; + return UCHAR_MAX + 1; + } + if (b1->slen > n) { + v = - (char) downcase (b1->data[n]); + if (v) return v; + return - (int) (UCHAR_MAX + 1); + } + return BSTR_OK; +} + +/* int bstrnicmp (const_bstring b0, const_bstring b1, int n) + * + * Compare two strings without differentiating between case for at most n + * characters. If the position where the two strings first differ is + * before the nth position, the return value is the difference of the values + * of the characters, otherwise 0 is returned. If the lengths are different + * and less than n characters, then a difference from 0 is given, but if the + * first extra character is '\0', then it is taken to be the value + * UCHAR_MAX+1. + */ +int bstrnicmp (const_bstring b0, const_bstring b1, int n) { +int i, v, m; + + if (bdata (b0) == NULL || b0->slen < 0 || + bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN; + m = n; + if (m > b0->slen) m = b0->slen; + if (m > b1->slen) m = b1->slen; + + if (b0->data != b1->data) { + for (i = 0; i < m; i ++) { + v = (char) downcase (b0->data[i]); + v -= (char) downcase (b1->data[i]); + if (v != 0) return b0->data[i] - b1->data[i]; + } + } + + if (n == m || b0->slen == b1->slen) return BSTR_OK; + + if (b0->slen > m) { + v = (char) downcase (b0->data[m]); + if (v) return v; + return UCHAR_MAX + 1; + } + + v = - (char) downcase (b1->data[m]); + if (v) return v; + return - (int) (UCHAR_MAX + 1); +} + +/* int biseqcaseless (const_bstring b0, const_bstring b1) + * + * Compare two strings for equality without differentiating between case. + * If the strings differ other than in case, 0 is returned, if the strings + * are the same, 1 is returned, if there is an error, -1 is returned. If + * the length of the strings are different, this function is O(1). '\0' + * termination characters are not treated in any special way. + */ +int biseqcaseless (const_bstring b0, const_bstring b1) { +int i, n; + + if (bdata (b0) == NULL || b0->slen < 0 || + bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR; + if (b0->slen != b1->slen) return BSTR_OK; + if (b0->data == b1->data || b0->slen == 0) return 1; + for (i=0, n=b0->slen; i < n; i++) { + if (b0->data[i] != b1->data[i]) { + unsigned char c = (unsigned char) downcase (b0->data[i]); + if (c != (unsigned char) downcase (b1->data[i])) return 0; + } + } + return 1; +} + +/* int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) + * + * Compare beginning of string b0 with a block of memory of length len + * without differentiating between case for equality. If the beginning of b0 + * differs from the memory block other than in case (or if b0 is too short), + * 0 is returned, if the strings are the same, 1 is returned, if there is an + * error, -1 is returned. '\0' characters are not treated in any special + * way. + */ +int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) { +int i; + + if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) + return BSTR_ERR; + if (b0->slen < len) return BSTR_OK; + if (b0->data == (const unsigned char *) blk || len == 0) return 1; + + for (i = 0; i < len; i ++) { + if (b0->data[i] != ((const unsigned char *) blk)[i]) { + if (downcase (b0->data[i]) != + downcase (((const unsigned char *) blk)[i])) return 0; + } + } + return 1; +} + +/* + * int bltrimws (bstring b) + * + * Delete whitespace contiguous from the left end of the string. + */ +int bltrimws (bstring b) { +int i, len; + + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + + for (len = b->slen, i = 0; i < len; i++) { + if (!wspace (b->data[i])) { + return bdelete (b, 0, i); + } + } + + b->data[0] = (unsigned char) '\0'; + b->slen = 0; + return BSTR_OK; +} + +/* + * int brtrimws (bstring b) + * + * Delete whitespace contiguous from the right end of the string. + */ +int brtrimws (bstring b) { +int i; + + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + + for (i = b->slen - 1; i >= 0; i--) { + if (!wspace (b->data[i])) { + if (b->mlen > i) b->data[i+1] = (unsigned char) '\0'; + b->slen = i + 1; + return BSTR_OK; + } + } + + b->data[0] = (unsigned char) '\0'; + b->slen = 0; + return BSTR_OK; +} + +/* + * int btrimws (bstring b) + * + * Delete whitespace contiguous from both ends of the string. + */ +int btrimws (bstring b) { +int i, j; + + if (b == NULL || b->data == NULL || b->mlen < b->slen || + b->slen < 0 || b->mlen <= 0) return BSTR_ERR; + + for (i = b->slen - 1; i >= 0; i--) { + if (!wspace (b->data[i])) { + if (b->mlen > i) b->data[i+1] = (unsigned char) '\0'; + b->slen = i + 1; + for (j = 0; wspace (b->data[j]); j++) {} + return bdelete (b, 0, j); + } + } + + b->data[0] = (unsigned char) '\0'; + b->slen = 0; + return BSTR_OK; +} + +/* int biseq (const_bstring b0, const_bstring b1) + * + * Compare the string b0 and b1. If the strings differ, 0 is returned, if + * the strings are the same, 1 is returned, if there is an error, -1 is + * returned. If the length of the strings are different, this function is + * O(1). '\0' termination characters are not treated in any special way. + */ +int biseq (const_bstring b0, const_bstring b1) { + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || + b0->slen < 0 || b1->slen < 0) return BSTR_ERR; + if (b0->slen != b1->slen) return BSTR_OK; + if (b0->data == b1->data || b0->slen == 0) return 1; + return !bstr__memcmp (b0->data, b1->data, b0->slen); +} + +/* int bisstemeqblk (const_bstring b0, const void * blk, int len) + * + * Compare beginning of string b0 with a block of memory of length len for + * equality. If the beginning of b0 differs from the memory block (or if b0 + * is too short), 0 is returned, if the strings are the same, 1 is returned, + * if there is an error, -1 is returned. '\0' characters are not treated in + * any special way. + */ +int bisstemeqblk (const_bstring b0, const void * blk, int len) { +int i; + + if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) + return BSTR_ERR; + if (b0->slen < len) return BSTR_OK; + if (b0->data == (const unsigned char *) blk || len == 0) return 1; + + for (i = 0; i < len; i ++) { + if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK; + } + return 1; +} + +/* int biseqcstr (const_bstring b, const char *s) + * + * Compare the bstring b and char * string s. The C string s must be '\0' + * terminated at exactly the length of the bstring b, and the contents + * between the two must be identical with the bstring b with no '\0' + * characters for the two contents to be considered equal. This is + * equivalent to the condition that their current contents will be always be + * equal when comparing them in the same format after converting one or the + * other. If the strings are equal 1 is returned, if they are unequal 0 is + * returned and if there is a detectable error BSTR_ERR is returned. + */ +int biseqcstr (const_bstring b, const char * s) { +int i; + if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR; + for (i=0; i < b->slen; i++) { + if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK; + } + return s[i] == '\0'; +} + +/* int biseqcstrcaseless (const_bstring b, const char *s) + * + * Compare the bstring b and char * string s. The C string s must be '\0' + * terminated at exactly the length of the bstring b, and the contents + * between the two must be identical except for case with the bstring b with + * no '\0' characters for the two contents to be considered equal. This is + * equivalent to the condition that their current contents will be always be + * equal ignoring case when comparing them in the same format after + * converting one or the other. If the strings are equal, except for case, + * 1 is returned, if they are unequal regardless of case 0 is returned and + * if there is a detectable error BSTR_ERR is returned. + */ +int biseqcstrcaseless (const_bstring b, const char * s) { +int i; + if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR; + for (i=0; i < b->slen; i++) { + if (s[i] == '\0' || + (b->data[i] != (unsigned char) s[i] && + downcase (b->data[i]) != (unsigned char) downcase (s[i]))) + return BSTR_OK; + } + return s[i] == '\0'; +} + +/* int bstrcmp (const_bstring b0, const_bstring b1) + * + * Compare the string b0 and b1. If there is an error, SHRT_MIN is returned, + * otherwise a value less than or greater than zero, indicating that the + * string pointed to by b0 is lexicographically less than or greater than + * the string pointed to by b1 is returned. If the the string lengths are + * unequal but the characters up until the length of the shorter are equal + * then a value less than, or greater than zero, indicating that the string + * pointed to by b0 is shorter or longer than the string pointed to by b1 is + * returned. 0 is returned if and only if the two strings are the same. If + * the length of the strings are different, this function is O(n). Like its + * standard C library counter part strcmp, the comparison does not proceed + * past any '\0' termination characters encountered. + */ +int bstrcmp (const_bstring b0, const_bstring b1) { +int i, v, n; + + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || + b0->slen < 0 || b1->slen < 0) return SHRT_MIN; + n = b0->slen; if (n > b1->slen) n = b1->slen; + if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0)) + return BSTR_OK; + + for (i = 0; i < n; i ++) { + v = ((char) b0->data[i]) - ((char) b1->data[i]); + if (v != 0) return v; + if (b0->data[i] == (unsigned char) '\0') return BSTR_OK; + } + + if (b0->slen > n) return 1; + if (b1->slen > n) return -1; + return BSTR_OK; +} + +/* int bstrncmp (const_bstring b0, const_bstring b1, int n) + * + * Compare the string b0 and b1 for at most n characters. If there is an + * error, SHRT_MIN is returned, otherwise a value is returned as if b0 and + * b1 were first truncated to at most n characters then bstrcmp was called + * with these new strings are paremeters. If the length of the strings are + * different, this function is O(n). Like its standard C library counter + * part strcmp, the comparison does not proceed past any '\0' termination + * characters encountered. + */ +int bstrncmp (const_bstring b0, const_bstring b1, int n) { +int i, v, m; + + if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || + b0->slen < 0 || b1->slen < 0) return SHRT_MIN; + m = n; + if (m > b0->slen) m = b0->slen; + if (m > b1->slen) m = b1->slen; + + if (b0->data != b1->data) { + for (i = 0; i < m; i ++) { + v = ((char) b0->data[i]) - ((char) b1->data[i]); + if (v != 0) return v; + if (b0->data[i] == (unsigned char) '\0') return BSTR_OK; + } + } + + if (n == m || b0->slen == b1->slen) return BSTR_OK; + + if (b0->slen > m) return 1; + return -1; +} + +/* bstring bmidstr (const_bstring b, int left, int len) + * + * Create a bstring which is the substring of b starting from position left + * and running for a length len (clamped by the end of the bstring b.) If + * b is detectably invalid, then NULL is returned. The section described + * by (left, len) is clamped to the boundaries of b. + */ +bstring bmidstr (const_bstring b, int left, int len) { + + if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; + + if (left < 0) { + len += left; + left = 0; + } + + if (len > b->slen - left) len = b->slen - left; + + if (len <= 0) return bfromcstr (""); + return blk2bstr (b->data + left, len); +} + +/* int bdelete (bstring b, int pos, int len) + * + * Removes characters from pos to pos+len-1 inclusive and shifts the tail of + * the bstring starting from pos+len to pos. len must be positive for this + * call to have any effect. The section of the string described by (pos, + * len) is clamped to boundaries of the bstring b. + */ +int bdelete (bstring b, int pos, int len) { + /* Clamp to left side of bstring */ + if (pos < 0) { + len += pos; + pos = 0; + } + + if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || + b->mlen < b->slen || b->mlen <= 0) + return BSTR_ERR; + if (len > 0 && pos < b->slen) { + if (pos + len >= b->slen) { + b->slen = pos; + } else { + bBlockCopy ((char *) (b->data + pos), + (char *) (b->data + pos + len), + b->slen - (pos+len)); + b->slen -= len; + } + b->data[b->slen] = (unsigned char) '\0'; + } + return BSTR_OK; +} + +/* int bdestroy (bstring b) + * + * Free up the bstring. Note that if b is detectably invalid or not writable + * then no action is performed and BSTR_ERR is returned. Like a freed memory + * allocation, dereferences, writes or any other action on b after it has + * been bdestroyed is undefined. + */ +int bdestroy (bstring b) { + if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen || + b->data == NULL) + return BSTR_ERR; + + bstr__free (b->data); + + /* In case there is any stale usage, there is one more chance to + notice this error. */ + + b->slen = -1; + b->mlen = -__LINE__; + b->data = NULL; + + bstr__free (b); + return BSTR_OK; +} + +/* int binstr (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * forward. If it is found then return with the first position where it is + * found, otherwise return BSTR_ERR. Note that this is just a brute force + * string searcher that does not attempt clever things like the Boyer-Moore + * search algorithm. Because of this there are many degenerate cases where + * this can take much longer than it needs to. + */ +int binstr (const_bstring b1, int pos, const_bstring b2) { +int j, ii, ll, lf; +unsigned char * d0; +unsigned char c0; +register unsigned char * d1; +register unsigned char c1; +register int i; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + /* No space to find such a string? */ + if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR; + + /* An obvious alias case */ + if (b1->data == b2->data && pos == 0) return 0; + + i = pos; + + d0 = b2->data; + d1 = b1->data; + ll = b2->slen; + + /* Peel off the b2->slen == 1 case */ + c0 = d0[0]; + if (1 == ll) { + for (;i < lf; i++) if (c0 == d1[i]) return i; + return BSTR_ERR; + } + + c1 = c0; + j = 0; + lf = b1->slen - 1; + + ii = -1; + if (i < lf) do { + /* Unrolled current character test */ + if (c1 != d1[i]) { + if (c1 != d1[1+i]) { + i += 2; + continue; + } + i++; + } + + /* Take note if this is the start of a potential match */ + if (0 == j) ii = i; + + /* Shift the test character down by one */ + j++; + i++; + + /* If this isn't past the last character continue */ + if (j < ll) { + c1 = d0[j]; + continue; + } + + N0:; + + /* If no characters mismatched, then we matched */ + if (i == ii+j) return ii; + + /* Shift back to the beginning */ + i -= j; + j = 0; + c1 = c0; + } while (i < lf); + + /* Deal with last case if unrolling caused a misalignment */ + if (i == lf && ll == j+1 && c1 == d1[i]) goto N0; + + return BSTR_ERR; +} + +/* int binstrr (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * backward. If it is found then return with the first position where it is + * found, otherwise return BSTR_ERR. Note that this is just a brute force + * string searcher that does not attempt clever things like the Boyer-Moore + * search algorithm. Because of this there are many degenerate cases where + * this can take much longer than it needs to. + */ +int binstrr (const_bstring b1, int pos, const_bstring b2) { +int j, i, l; +unsigned char * d0, * d1; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos && b2->slen == 0) return pos; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + /* Obvious alias case */ + if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0; + + i = pos; + if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR; + + /* If no space to find such a string then snap back */ + if (l + 1 <= i) i = l; + j = 0; + + d0 = b2->data; + d1 = b1->data; + l = b2->slen; + + for (;;) { + if (d0[j] == d1[i + j]) { + j ++; + if (j >= l) return i; + } else { + i --; + if (i < 0) break; + j=0; + } + } + + return BSTR_ERR; +} + +/* int binstrcaseless (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * forward but without regard to case. If it is found then return with the + * first position where it is found, otherwise return BSTR_ERR. Note that + * this is just a brute force string searcher that does not attempt clever + * things like the Boyer-Moore search algorithm. Because of this there are + * many degenerate cases where this can take much longer than it needs to. + */ +int binstrcaseless (const_bstring b1, int pos, const_bstring b2) { +int j, i, l, ll; +unsigned char * d0, * d1; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + l = b1->slen - b2->slen + 1; + + /* No space to find such a string? */ + if (l <= pos) return BSTR_ERR; + + /* An obvious alias case */ + if (b1->data == b2->data && pos == 0) return BSTR_OK; + + i = pos; + j = 0; + + d0 = b2->data; + d1 = b1->data; + ll = b2->slen; + + for (;;) { + if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) { + j ++; + if (j >= ll) return i; + } else { + i ++; + if (i >= l) break; + j=0; + } + } + + return BSTR_ERR; +} + +/* int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) + * + * Search for the bstring b2 in b1 starting from position pos, and searching + * backward but without regard to case. If it is found then return with the + * first position where it is found, otherwise return BSTR_ERR. Note that + * this is just a brute force string searcher that does not attempt clever + * things like the Boyer-Moore search algorithm. Because of this there are + * many degenerate cases where this can take much longer than it needs to. + */ +int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) { +int j, i, l; +unsigned char * d0, * d1; + + if (b1 == NULL || b1->data == NULL || b1->slen < 0 || + b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; + if (b1->slen == pos && b2->slen == 0) return pos; + if (b1->slen < pos || pos < 0) return BSTR_ERR; + if (b2->slen == 0) return pos; + + /* Obvious alias case */ + if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK; + + i = pos; + if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR; + + /* If no space to find such a string then snap back */ + if (l + 1 <= i) i = l; + j = 0; + + d0 = b2->data; + d1 = b1->data; + l = b2->slen; + + for (;;) { + if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) { + j ++; + if (j >= l) return i; + } else { + i --; + if (i < 0) break; + j=0; + } + } + + return BSTR_ERR; +} + + +/* int bstrchrp (const_bstring b, int c, int pos) + * + * Search for the character c in b forwards from the position pos + * (inclusive). + */ +int bstrchrp (const_bstring b, int c, int pos) { +unsigned char * p; + + if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR; + p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos)); + if (p) return (int) (p - b->data); + return BSTR_ERR; +} + +/* int bstrrchrp (const_bstring b, int c, int pos) + * + * Search for the character c in b backwards from the position pos in string + * (inclusive). + */ +int bstrrchrp (const_bstring b, int c, int pos) { +int i; + + if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR; + for (i=pos; i >= 0; i--) { + if (b->data[i] == (unsigned char) c) return i; + } + return BSTR_ERR; +} + +#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF) +#define LONG_LOG_BITS_QTY (3) +#define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY) +#define LONG_TYPE unsigned char + +#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY) +struct charField { LONG_TYPE content[CFCLEN]; }; +#define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1)))) +#define setInCharField(cf,idx) { \ + unsigned int c = (unsigned int) (idx); \ + (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \ +} + +#else + +#define CFCLEN (1 << CHAR_BIT) +struct charField { unsigned char content[CFCLEN]; }; +#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)]) +#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0 + +#endif + +/* Convert a bstring to charField */ +static int buildCharField (struct charField * cf, const_bstring b) { +int i; + if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR; + memset ((void *) cf->content, 0, sizeof (struct charField)); + for (i=0; i < b->slen; i++) { + setInCharField (cf, b->data[i]); + } + return BSTR_OK; +} + +static void invertCharField (struct charField * cf) { +int i; + for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i]; +} + +/* Inner engine for binchr */ +static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) { +int i; + for (i=pos; i < len; i++) { + unsigned char c = (unsigned char) data[i]; + if (testInCharField (cf, c)) return i; + } + return BSTR_ERR; +} + +/* int binchr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the first position in b0 starting from pos or after, in which + * one of the characters in b1 is found and return it. If such a position + * does not exist in b0, then BSTR_ERR is returned. + */ +int binchr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || + b0->slen <= pos) return BSTR_ERR; + if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos); + if (0 > buildCharField (&chrs, b1)) return BSTR_ERR; + return binchrCF (b0->data, b0->slen, pos, &chrs); +} + +/* Inner engine for binchrr */ +static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) { +int i; + for (i=pos; i >= 0; i--) { + unsigned int c = (unsigned int) data[i]; + if (testInCharField (cf, c)) return i; + } + return BSTR_ERR; +} + +/* int binchrr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the last position in b0 no greater than pos, in which one of + * the characters in b1 is found and return it. If such a position does not + * exist in b0, then BSTR_ERR is returned. + */ +int binchrr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL || + b0->slen < pos) return BSTR_ERR; + if (pos == b0->slen) pos--; + if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos); + if (0 > buildCharField (&chrs, b1)) return BSTR_ERR; + return binchrrCF (b0->data, pos, &chrs); +} + +/* int bninchr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the first position in b0 starting from pos or after, in which + * none of the characters in b1 is found and return it. If such a position + * does not exist in b0, then BSTR_ERR is returned. + */ +int bninchr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || + b0->slen <= pos) return BSTR_ERR; + if (buildCharField (&chrs, b1) < 0) return BSTR_ERR; + invertCharField (&chrs); + return binchrCF (b0->data, b0->slen, pos, &chrs); +} + +/* int bninchrr (const_bstring b0, int pos, const_bstring b1); + * + * Search for the last position in b0 no greater than pos, in which none of + * the characters in b1 is found and return it. If such a position does not + * exist in b0, then BSTR_ERR is returned. + */ +int bninchrr (const_bstring b0, int pos, const_bstring b1) { +struct charField chrs; + if (pos < 0 || b0 == NULL || b0->data == NULL || + b0->slen < pos) return BSTR_ERR; + if (pos == b0->slen) pos--; + if (buildCharField (&chrs, b1) < 0) return BSTR_ERR; + invertCharField (&chrs); + return binchrrCF (b0->data, pos, &chrs); +} + +/* int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill) + * + * Overwrite the string b0 starting at position pos with the string b1. If + * the position pos is past the end of b0, then the character "fill" is + * appended as necessary to make up the gap between the end of b0 and pos. + * If b1 is NULL, it behaves as if it were a 0-length string. + */ +int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) { +int d, newlen; +ptrdiff_t pd; +bstring aux = (bstring) b1; + + if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || + b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR; + if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR; + + d = pos; + + /* Aliasing case */ + if (NULL != aux) { + if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) { + if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; + } + d += aux->slen; + } + + /* Increase memory size if necessary */ + if (balloc (b0, d + 1) != BSTR_OK) { + if (aux != b1) bdestroy (aux); + return BSTR_ERR; + } + + newlen = b0->slen; + + /* Fill in "fill" character as necessary */ + if (pos > newlen) { + bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen)); + newlen = pos; + } + + /* Copy b1 to position pos in b0. */ + if (aux != NULL) { + bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen); + if (aux != b1) bdestroy (aux); + } + + /* Indicate the potentially increased size of b0 */ + if (d > newlen) newlen = d; + + b0->slen = newlen; + b0->data[newlen] = (unsigned char) '\0'; + + return BSTR_OK; +} + +/* int binsert (bstring b1, int pos, bstring b2, unsigned char fill) + * + * Inserts the string b2 into b1 at position pos. If the position pos is + * past the end of b1, then the character "fill" is appended as necessary to + * make up the gap between the end of b1 and pos. Unlike bsetstr, binsert + * does not allow b2 to be NULL. + */ +int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) { +int d, l; +ptrdiff_t pd; +bstring aux = (bstring) b2; + + if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || + b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR; + + /* Aliasing case */ + if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) { + if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR; + } + + /* Compute the two possible end pointers */ + d = b1->slen + aux->slen; + l = pos + aux->slen; + if ((d|l) < 0) return BSTR_ERR; + + if (l > d) { + /* Inserting past the end of the string */ + if (balloc (b1, l + 1) != BSTR_OK) { + if (aux != b2) bdestroy (aux); + return BSTR_ERR; + } + bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen)); + b1->slen = l; + } else { + /* Inserting in the middle of the string */ + if (balloc (b1, d + 1) != BSTR_OK) { + if (aux != b2) bdestroy (aux); + return BSTR_ERR; + } + bBlockCopy (b1->data + l, b1->data + pos, d - l); + b1->slen = d; + } + bBlockCopy (b1->data + pos, aux->data, aux->slen); + b1->data[b1->slen] = (unsigned char) '\0'; + if (aux != b2) bdestroy (aux); + return BSTR_OK; +} + +/* int breplace (bstring b1, int pos, int len, bstring b2, + * unsigned char fill) + * + * Replace a section of a string from pos for a length len with the string b2. + * fill is used is pos > b1->slen. + */ +int breplace (bstring b1, int pos, int len, const_bstring b2, + unsigned char fill) { +int pl, ret; +ptrdiff_t pd; +bstring aux = (bstring) b2; + + if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL || + b2 == NULL || b1->data == NULL || b2->data == NULL || + b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen || + b1->mlen <= 0) return BSTR_ERR; + + /* Straddles the end? */ + if (pl >= b1->slen) { + if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret; + if (pos + b2->slen < b1->slen) { + b1->slen = pos + b2->slen; + b1->data[b1->slen] = (unsigned char) '\0'; + } + return ret; + } + + /* Aliasing case */ + if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) { + if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR; + } + + if (aux->slen > len) { + if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) { + if (aux != b2) bdestroy (aux); + return BSTR_ERR; + } + } + + if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len)); + bstr__memcpy (b1->data + pos, aux->data, aux->slen); + b1->slen += aux->slen - len; + b1->data[b1->slen] = (unsigned char) '\0'; + if (aux != b2) bdestroy (aux); + return BSTR_OK; +} + +/* + * findreplaceengine is used to implement bfindreplace and + * bfindreplacecaseless. It works by breaking the three cases of + * expansion, reduction and replacement, and solving each of these + * in the most efficient way possible. + */ + +typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2); + +#define INITIAL_STATIC_FIND_INDEX_COUNT 32 + +static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) { +int i, ret, slen, mlen, delta, acc; +int * d; +int static_d[INITIAL_STATIC_FIND_INDEX_COUNT+1]; /* This +1 is unnecessary, but it shuts up LINT. */ +ptrdiff_t pd; +bstring auxf = (bstring) find; +bstring auxr = (bstring) repl; + + if (b == NULL || b->data == NULL || find == NULL || + find->data == NULL || repl == NULL || repl->data == NULL || + pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen || + b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR; + if (pos > b->slen - find->slen) return BSTR_OK; + + /* Alias with find string */ + pd = (ptrdiff_t) (find->data - b->data); + if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) { + if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR; + } + + /* Alias with repl string */ + pd = (ptrdiff_t) (repl->data - b->data); + if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) { + if (NULL == (auxr = bstrcpy (repl))) { + if (auxf != find) bdestroy (auxf); + return BSTR_ERR; + } + } + + delta = auxf->slen - auxr->slen; + + /* in-place replacement since find and replace strings are of equal + length */ + if (delta == 0) { + while ((pos = instr (b, pos, auxf)) >= 0) { + bstr__memcpy (b->data + pos, auxr->data, auxr->slen); + pos += auxf->slen; + } + if (auxf != find) bdestroy (auxf); + if (auxr != repl) bdestroy (auxr); + return BSTR_OK; + } + + /* shrinking replacement since auxf->slen > auxr->slen */ + if (delta > 0) { + acc = 0; + + while ((i = instr (b, pos, auxf)) >= 0) { + if (acc && i > pos) + bstr__memmove (b->data + pos - acc, b->data + pos, i - pos); + if (auxr->slen) + bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen); + acc += delta; + pos = i + auxf->slen; + } + + if (acc) { + i = b->slen; + if (i > pos) + bstr__memmove (b->data + pos - acc, b->data + pos, i - pos); + b->slen -= acc; + b->data[b->slen] = (unsigned char) '\0'; + } + + if (auxf != find) bdestroy (auxf); + if (auxr != repl) bdestroy (auxr); + return BSTR_OK; + } + + /* expanding replacement since find->slen < repl->slen. Its a lot + more complicated. This works by first finding all the matches and + storing them to a growable array, then doing at most one resize of + the destination bstring and then performing the direct memory transfers + of the string segment pieces to form the final result. The growable + array of matches uses a deferred doubling reallocing strategy. What + this means is that it starts as a reasonably fixed sized auto array in + the hopes that many if not most cases will never need to grow this + array. But it switches as soon as the bounds of the array will be + exceeded. An extra find result is always appended to this array that + corresponds to the end of the destination string, so slen is checked + against mlen - 1 rather than mlen before resizing. + */ + + mlen = INITIAL_STATIC_FIND_INDEX_COUNT; + d = (int *) static_d; /* Avoid malloc for trivial/initial cases */ + acc = slen = 0; + + while ((pos = instr (b, pos, auxf)) >= 0) { + if (slen >= mlen - 1) { + int sl, *t; + + mlen += mlen; + sl = sizeof (int *) * mlen; + if (static_d == d) d = NULL; /* static_d cannot be realloced */ + if (mlen <= 0 || sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) { + ret = BSTR_ERR; + goto done; + } + if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d)); + d = t; + } + d[slen] = pos; + slen++; + acc -= delta; + pos += auxf->slen; + if (pos < 0 || acc < 0) { + ret = BSTR_ERR; + goto done; + } + } + + /* slen <= INITIAL_STATIC_INDEX_COUNT-1 or mlen-1 here. */ + d[slen] = b->slen; + + if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) { + b->slen += acc; + for (i = slen-1; i >= 0; i--) { + int s, l; + s = d[i] + auxf->slen; + l = d[i+1] - s; /* d[slen] may be accessed here. */ + if (l) { + bstr__memmove (b->data + s + acc, b->data + s, l); + } + if (auxr->slen) { + bstr__memmove (b->data + s + acc - auxr->slen, + auxr->data, auxr->slen); + } + acc += delta; + } + b->data[b->slen] = (unsigned char) '\0'; + } + + done:; + if (static_d == d) d = NULL; + bstr__free (d); + if (auxf != find) bdestroy (auxf); + if (auxr != repl) bdestroy (auxr); + return ret; +} + +/* int bfindreplace (bstring b, const_bstring find, const_bstring repl, + * int pos) + * + * Replace all occurrences of a find string with a replace string after a + * given point in a bstring. + */ +int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) { + return findreplaceengine (b, find, repl, pos, binstr); +} + +/* int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, + * int pos) + * + * Replace all occurrences of a find string, ignoring case, with a replace + * string after a given point in a bstring. + */ +int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) { + return findreplaceengine (b, find, repl, pos, binstrcaseless); +} + +/* int binsertch (bstring b, int pos, int len, unsigned char fill) + * + * Inserts the character fill repeatedly into b at position pos for a + * length len. If the position pos is past the end of b, then the + * character "fill" is appended as necessary to make up the gap between the + * end of b and the position pos + len. + */ +int binsertch (bstring b, int pos, int len, unsigned char fill) { +int d, l, i; + + if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || len < 0) return BSTR_ERR; + + /* Compute the two possible end pointers */ + d = b->slen + len; + l = pos + len; + if ((d|l) < 0) return BSTR_ERR; + + if (l > d) { + /* Inserting past the end of the string */ + if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR; + pos = b->slen; + b->slen = l; + } else { + /* Inserting in the middle of the string */ + if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR; + for (i = d - 1; i >= l; i--) { + b->data[i] = b->data[i - len]; + } + b->slen = d; + } + + for (i=pos; i < l; i++) b->data[i] = fill; + b->data[b->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bpattern (bstring b, int len) + * + * Replicate the bstring, b in place, end to end repeatedly until it + * surpasses len characters, then chop the result to exactly len characters. + * This function operates in-place. The function will return with BSTR_ERR + * if b is NULL or of length 0, otherwise BSTR_OK is returned. + */ +int bpattern (bstring b, int len) { +int i, d; + + d = blength (b); + if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR; + if (len > 0) { + if (d == 1) return bsetstr (b, len, NULL, b->data[0]); + for (i = d; i < len; i++) b->data[i] = b->data[i - d]; + } + b->data[len] = (unsigned char) '\0'; + b->slen = len; + return BSTR_OK; +} + +#define BS_BUFF_SZ (1024) + +/* int breada (bstring b, bNread readPtr, void * parm) + * + * Use a finite buffer fread-like function readPtr to concatenate to the + * bstring b the entire contents of file-like source data in a roughly + * efficient way. + */ +int breada (bstring b, bNread readPtr, void * parm) { +int i, l, n; + + if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || readPtr == NULL) return BSTR_ERR; + + i = b->slen; + for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) { + if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR; + l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm); + i += l; + b->slen = i; + if (i < n) break; + } + + b->data[i] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* bstring bread (bNread readPtr, void * parm) + * + * Use a finite buffer fread-like function readPtr to create a bstring + * filled with the entire contents of file-like source data in a roughly + * efficient way. + */ +bstring bread (bNread readPtr, void * parm) { +bstring buff; + + if (0 > breada (buff = bfromcstr (""), readPtr, parm)) { + bdestroy (buff); + return NULL; + } + return buff; +} + +/* int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) + * + * Use an fgetc-like single character stream reading function (getcPtr) to + * obtain a sequence of characters which are concatenated to the end of the + * bstring b. The stream read is terminated by the passed in terminator + * parameter. + * + * If getcPtr returns with a negative number, or the terminator character + * (which is appended) is read, then the stream reading is halted and the + * function returns with a partial result in b. If there is an empty partial + * result, 1 is returned. If no characters are read, or there is some other + * detectable error, BSTR_ERR is returned. + */ +int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) { +int c, d, e; + + if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR; + d = 0; + e = b->mlen - 2; + + while ((c = getcPtr (parm)) >= 0) { + if (d > e) { + b->slen = d; + if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; + e = b->mlen - 2; + } + b->data[d] = (unsigned char) c; + d++; + if (c == terminator) break; + } + + b->data[d] = (unsigned char) '\0'; + b->slen = d; + + return d == 0 && c < 0; +} + +/* int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) + * + * Use an fgetc-like single character stream reading function (getcPtr) to + * obtain a sequence of characters which are concatenated to the end of the + * bstring b. The stream read is terminated by the passed in terminator + * parameter. + * + * If getcPtr returns with a negative number, or the terminator character + * (which is appended) is read, then the stream reading is halted and the + * function returns with a partial result concatentated to b. If there is + * an empty partial result, 1 is returned. If no characters are read, or + * there is some other detectable error, BSTR_ERR is returned. + */ +int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) { +int c, d, e; + + if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || + b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR; + d = b->slen; + e = b->mlen - 2; + + while ((c = getcPtr (parm)) >= 0) { + if (d > e) { + b->slen = d; + if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; + e = b->mlen - 2; + } + b->data[d] = (unsigned char) c; + d++; + if (c == terminator) break; + } + + b->data[d] = (unsigned char) '\0'; + b->slen = d; + + return d == 0 && c < 0; +} + +/* bstring bgets (bNgetc getcPtr, void * parm, char terminator) + * + * Use an fgetc-like single character stream reading function (getcPtr) to + * obtain a sequence of characters which are concatenated into a bstring. + * The stream read is terminated by the passed in terminator function. + * + * If getcPtr returns with a negative number, or the terminator character + * (which is appended) is read, then the stream reading is halted and the + * result obtained thus far is returned. If no characters are read, or + * there is some other detectable error, NULL is returned. + */ +bstring bgets (bNgetc getcPtr, void * parm, char terminator) { +bstring buff; + + if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) { + bdestroy (buff); + buff = NULL; + } + return buff; +} + +struct bStream { + bstring buff; /* Buffer for over-reads */ + void * parm; /* The stream handle for core stream */ + bNread readFnPtr; /* fread compatible fnptr for core stream */ + int isEOF; /* track file's EOF state */ + int maxBuffSz; +}; + +/* struct bStream * bsopen (bNread readPtr, void * parm) + * + * Wrap a given open stream (described by a fread compatible function + * pointer and stream handle) into an open bStream suitable for the bstring + * library streaming functions. + */ +struct bStream * bsopen (bNread readPtr, void * parm) { +struct bStream * s; + + if (readPtr == NULL) return NULL; + s = (struct bStream *) bstr__alloc (sizeof (struct bStream)); + if (s == NULL) return NULL; + s->parm = parm; + s->buff = bfromcstr (""); + s->readFnPtr = readPtr; + s->maxBuffSz = BS_BUFF_SZ; + s->isEOF = 0; + return s; +} + +/* int bsbufflength (struct bStream * s, int sz) + * + * Set the length of the buffer used by the bStream. If sz is zero, the + * length is not set. This function returns with the previous length. + */ +int bsbufflength (struct bStream * s, int sz) { +int oldSz; + if (s == NULL || sz < 0) return BSTR_ERR; + oldSz = s->maxBuffSz; + if (sz > 0) s->maxBuffSz = sz; + return oldSz; +} + +int bseof (const struct bStream * s) { + if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR; + return s->isEOF && (s->buff->slen == 0); +} + +/* void * bsclose (struct bStream * s) + * + * Close the bStream, and return the handle to the stream that was originally + * used to open the given stream. + */ +void * bsclose (struct bStream * s) { +void * parm; + if (s == NULL) return NULL; + s->readFnPtr = NULL; + if (s->buff) bdestroy (s->buff); + s->buff = NULL; + parm = s->parm; + s->parm = NULL; + s->isEOF = 1; + bstr__free (s); + return parm; +} + +/* int bsreadlna (bstring r, struct bStream * s, char terminator) + * + * Read a bstring terminated by the terminator character or the end of the + * stream from the bStream (s) and return it into the parameter r. This + * function may read additional characters from the core stream that are not + * returned, but will be retained for subsequent read operations. + */ +int bsreadlna (bstring r, struct bStream * s, char terminator) { +int i, l, ret, rlo; +char * b; +struct tagbstring x; + + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 || + r->slen < 0 || r->mlen < r->slen) return BSTR_ERR; + l = s->buff->slen; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + b = (char *) s->buff->data; + x.data = (unsigned char *) b; + + /* First check if the current buffer holds the terminator */ + b[l] = terminator; /* Set sentinel */ + for (i=0; b[i] != terminator; i++) ; + if (i < l) { + x.slen = i + 1; + ret = bconcat (r, &x); + s->buff->slen = l; + if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1); + return BSTR_OK; + } + + rlo = r->slen; + + /* If not then just concatenate the entire buffer to the output */ + x.slen = l; + if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR; + + /* Perform direct in-place reads into the destination to allow for + the minimum of data-copies */ + for (;;) { + if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR; + b = (char *) (r->data + r->slen); + l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm); + if (l <= 0) { + r->data[r->slen] = (unsigned char) '\0'; + s->buff->slen = 0; + s->isEOF = 1; + /* If nothing was read return with an error message */ + return BSTR_ERR & -(r->slen == rlo); + } + b[l] = terminator; /* Set sentinel */ + for (i=0; b[i] != terminator; i++) ; + if (i < l) break; + r->slen += l; + } + + /* Terminator found, push over-read back to buffer */ + i++; + r->slen += i; + s->buff->slen = l - i; + bstr__memcpy (s->buff->data, b + i, l - i); + r->data[r->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bsreadlnsa (bstring r, struct bStream * s, bstring term) + * + * Read a bstring terminated by any character in the term string or the end + * of the stream from the bStream (s) and return it into the parameter r. + * This function may read additional characters from the core stream that + * are not returned, but will be retained for subsequent read operations. + */ +int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) { +int i, l, ret, rlo; +unsigned char * b; +struct tagbstring x; +struct charField cf; + + if (s == NULL || s->buff == NULL || r == NULL || term == NULL || + term->data == NULL || r->mlen <= 0 || r->slen < 0 || + r->mlen < r->slen) return BSTR_ERR; + if (term->slen == 1) return bsreadlna (r, s, term->data[0]); + if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR; + + l = s->buff->slen; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + b = (unsigned char *) s->buff->data; + x.data = b; + + /* First check if the current buffer holds the terminator */ + b[l] = term->data[0]; /* Set sentinel */ + for (i=0; !testInCharField (&cf, b[i]); i++) ; + if (i < l) { + x.slen = i + 1; + ret = bconcat (r, &x); + s->buff->slen = l; + if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1); + return BSTR_OK; + } + + rlo = r->slen; + + /* If not then just concatenate the entire buffer to the output */ + x.slen = l; + if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR; + + /* Perform direct in-place reads into the destination to allow for + the minimum of data-copies */ + for (;;) { + if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR; + b = (unsigned char *) (r->data + r->slen); + l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm); + if (l <= 0) { + r->data[r->slen] = (unsigned char) '\0'; + s->buff->slen = 0; + s->isEOF = 1; + /* If nothing was read return with an error message */ + return BSTR_ERR & -(r->slen == rlo); + } + + b[l] = term->data[0]; /* Set sentinel */ + for (i=0; !testInCharField (&cf, b[i]); i++) ; + if (i < l) break; + r->slen += l; + } + + /* Terminator found, push over-read back to buffer */ + i++; + r->slen += i; + s->buff->slen = l - i; + bstr__memcpy (s->buff->data, b + i, l - i); + r->data[r->slen] = (unsigned char) '\0'; + return BSTR_OK; +} + +/* int bsreada (bstring r, struct bStream * s, int n) + * + * Read a bstring of length n (or, if it is fewer, as many bytes as is + * remaining) from the bStream. This function may read additional + * characters from the core stream that are not returned, but will be + * retained for subsequent read operations. This function will not read + * additional characters from the core stream beyond virtual stream pointer. + */ +int bsreada (bstring r, struct bStream * s, int n) { +int l, ret, orslen; +char * b; +struct tagbstring x; + + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 + || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR; + + n += r->slen; + if (n <= 0) return BSTR_ERR; + + l = s->buff->slen; + + orslen = r->slen; + + if (0 == l) { + if (s->isEOF) return BSTR_ERR; + if (r->mlen > n) { + l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm); + if (0 >= l || l > n - r->slen) { + s->isEOF = 1; + return BSTR_ERR; + } + r->slen += l; + r->data[r->slen] = (unsigned char) '\0'; + return 0; + } + } + + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + b = (char *) s->buff->data; + x.data = (unsigned char *) b; + + do { + if (l + r->slen >= n) { + x.slen = n - r->slen; + ret = bconcat (r, &x); + s->buff->slen = l; + if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen); + return BSTR_ERR & -(r->slen == orslen); + } + + x.slen = l; + if (BSTR_OK != bconcat (r, &x)) break; + + l = n - r->slen; + if (l > s->maxBuffSz) l = s->maxBuffSz; + + l = (int) s->readFnPtr (b, 1, l, s->parm); + + } while (l > 0); + if (l < 0) l = 0; + if (l == 0) s->isEOF = 1; + s->buff->slen = l; + return BSTR_ERR & -(r->slen == orslen); +} + +/* int bsreadln (bstring r, struct bStream * s, char terminator) + * + * Read a bstring terminated by the terminator character or the end of the + * stream from the bStream (s) and return it into the parameter r. This + * function may read additional characters from the core stream that are not + * returned, but will be retained for subsequent read operations. + */ +int bsreadln (bstring r, struct bStream * s, char terminator) { + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0) + return BSTR_ERR; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + r->slen = 0; + return bsreadlna (r, s, terminator); +} + +/* int bsreadlns (bstring r, struct bStream * s, bstring term) + * + * Read a bstring terminated by any character in the term string or the end + * of the stream from the bStream (s) and return it into the parameter r. + * This function may read additional characters from the core stream that + * are not returned, but will be retained for subsequent read operations. + */ +int bsreadlns (bstring r, struct bStream * s, const_bstring term) { + if (s == NULL || s->buff == NULL || r == NULL || term == NULL + || term->data == NULL || r->mlen <= 0) return BSTR_ERR; + if (term->slen == 1) return bsreadln (r, s, term->data[0]); + if (term->slen < 1) return BSTR_ERR; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + r->slen = 0; + return bsreadlnsa (r, s, term); +} + +/* int bsread (bstring r, struct bStream * s, int n) + * + * Read a bstring of length n (or, if it is fewer, as many bytes as is + * remaining) from the bStream. This function may read additional + * characters from the core stream that are not returned, but will be + * retained for subsequent read operations. This function will not read + * additional characters from the core stream beyond virtual stream pointer. + */ +int bsread (bstring r, struct bStream * s, int n) { + if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 + || n <= 0) return BSTR_ERR; + if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; + r->slen = 0; + return bsreada (r, s, n); +} + +/* int bsunread (struct bStream * s, const_bstring b) + * + * Insert a bstring into the bStream at the current position. These + * characters will be read prior to those that actually come from the core + * stream. + */ +int bsunread (struct bStream * s, const_bstring b) { + if (s == NULL || s->buff == NULL) return BSTR_ERR; + return binsert (s->buff, 0, b, (unsigned char) '?'); +} + +/* int bspeek (bstring r, const struct bStream * s) + * + * Return the currently buffered characters from the bStream that will be + * read prior to reads from the core stream. + */ +int bspeek (bstring r, const struct bStream * s) { + if (s == NULL || s->buff == NULL) return BSTR_ERR; + return bassign (r, s->buff); +} + +/* bstring bjoin (const struct bstrList * bl, const_bstring sep); + * + * Join the entries of a bstrList into one bstring by sequentially + * concatenating them with the sep string in between. If there is an error + * NULL is returned, otherwise a bstring with the correct result is returned. + */ +bstring bjoin (const struct bstrList * bl, const_bstring sep) { +bstring b; +int i, c, v; + + if (bl == NULL || bl->qty < 0) return NULL; + if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL; + + for (i = 0, c = 1; i < bl->qty; i++) { + v = bl->entry[i]->slen; + if (v < 0) return NULL; /* Invalid input */ + c += v; + if (c < 0) return NULL; /* Wrap around ?? */ + } + + if (sep != NULL) c += (bl->qty - 1) * sep->slen; + + b = (bstring) bstr__alloc (sizeof (struct tagbstring)); + if (NULL == b) return NULL; /* Out of memory */ + b->data = (unsigned char *) bstr__alloc (c); + if (b->data == NULL) { + bstr__free (b); + return NULL; + } + + b->mlen = c; + b->slen = c-1; + + for (i = 0, c = 0; i < bl->qty; i++) { + if (i > 0 && sep != NULL) { + bstr__memcpy (b->data + c, sep->data, sep->slen); + c += sep->slen; + } + v = bl->entry[i]->slen; + bstr__memcpy (b->data + c, bl->entry[i]->data, v); + c += v; + } + b->data[c] = (unsigned char) '\0'; + return b; +} + +#define BSSSC_BUFF_LEN (256) + +/* int bssplitscb (struct bStream * s, const_bstring splitStr, + * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) + * + * Iterate the set of disjoint sequential substrings read from a stream + * divided by any of the characters in splitStr. An empty splitStr causes + * the whole stream to be iterated once. + * + * Note: At the point of calling the cb function, the bStream pointer is + * pointed exactly at the position right after having read the split + * character. The cb function can act on the stream by causing the bStream + * pointer to move, and bssplitscb will continue by starting the next split + * at the position of the pointer after the return from cb. + * + * However, if the cb causes the bStream s to be destroyed then the cb must + * return with a negative value, otherwise bssplitscb will continue in an + * undefined manner. + */ +int bssplitscb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) { +struct charField chrs; +bstring buff; +int i, p, ret; + + if (cb == NULL || s == NULL || s->readFnPtr == NULL + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + + if (NULL == (buff = bfromcstr (""))) return BSTR_ERR; + + if (splitStr->slen == 0) { + while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ; + if ((ret = cb (parm, 0, buff)) > 0) + ret = 0; + } else { + buildCharField (&chrs, splitStr); + ret = p = i = 0; + for (;;) { + if (i >= buff->slen) { + bsreada (buff, s, BSSSC_BUFF_LEN); + if (i >= buff->slen) { + if (0 < (ret = cb (parm, p, buff))) ret = 0; + break; + } + } + if (testInCharField (&chrs, buff->data[i])) { + struct tagbstring t; + unsigned char c; + + blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1)); + if ((ret = bsunread (s, &t)) < 0) break; + buff->slen = i; + c = buff->data[i]; + buff->data[i] = (unsigned char) '\0'; + if ((ret = cb (parm, p, buff)) < 0) break; + buff->data[i] = c; + buff->slen = 0; + p += i + 1; + i = -1; + } + i++; + } + } + + bdestroy (buff); + return ret; +} + +/* int bssplitstrcb (struct bStream * s, const_bstring splitStr, + * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) + * + * Iterate the set of disjoint sequential substrings read from a stream + * divided by the entire substring splitStr. An empty splitStr causes + * each character of the stream to be iterated. + * + * Note: At the point of calling the cb function, the bStream pointer is + * pointed exactly at the position right after having read the split + * character. The cb function can act on the stream by causing the bStream + * pointer to move, and bssplitscb will continue by starting the next split + * at the position of the pointer after the return from cb. + * + * However, if the cb causes the bStream s to be destroyed then the cb must + * return with a negative value, otherwise bssplitscb will continue in an + * undefined manner. + */ +int bssplitstrcb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) { +bstring buff; +int i, p, ret; + + if (cb == NULL || s == NULL || s->readFnPtr == NULL + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + + if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm); + + if (NULL == (buff = bfromcstr (""))) return BSTR_ERR; + + if (splitStr->slen == 0) { + for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) { + if ((ret = cb (parm, 0, buff)) < 0) { + bdestroy (buff); + return ret; + } + buff->slen = 0; + } + return BSTR_OK; + } else { + ret = p = i = 0; + for (i=p=0;;) { + if ((ret = binstr (buff, 0, splitStr)) >= 0) { + struct tagbstring t; + blk2tbstr (t, buff->data, ret); + i = ret + splitStr->slen; + if ((ret = cb (parm, p, &t)) < 0) break; + p += i; + bdelete (buff, 0, i); + } else { + bsreada (buff, s, BSSSC_BUFF_LEN); + if (bseof (s)) { + if ((ret = cb (parm, p, buff)) > 0) ret = 0; + break; + } + } + } + } + + bdestroy (buff); + return ret; +} + +/* int bstrListCreate (void) + * + * Create a bstrList. + */ +struct bstrList * bstrListCreate (void) { +struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (sl) { + sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring)); + if (!sl->entry) { + bstr__free (sl); + sl = NULL; + } else { + sl->qty = 0; + sl->mlen = 1; + } + } + return sl; +} + +/* int bstrListDestroy (struct bstrList * sl) + * + * Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate. + */ +int bstrListDestroy (struct bstrList * sl) { +int i; + if (sl == NULL || sl->qty < 0) return BSTR_ERR; + for (i=0; i < sl->qty; i++) { + if (sl->entry[i]) { + bdestroy (sl->entry[i]); + sl->entry[i] = NULL; + } + } + sl->qty = -1; + sl->mlen = -1; + bstr__free (sl->entry); + sl->entry = NULL; + bstr__free (sl); + return BSTR_OK; +} + +/* int bstrListAlloc (struct bstrList * sl, int msz) + * + * Ensure that there is memory for at least msz number of entries for the + * list. + */ +int bstrListAlloc (struct bstrList * sl, int msz) { +bstring * l; +int smsz; +size_t nsz; + if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR; + if (sl->mlen >= msz) return BSTR_OK; + smsz = snapUpSize (msz); + nsz = ((size_t) smsz) * sizeof (bstring); + if (nsz < (size_t) smsz) return BSTR_ERR; + l = (bstring *) bstr__realloc (sl->entry, nsz); + if (!l) { + smsz = msz; + nsz = ((size_t) smsz) * sizeof (bstring); + l = (bstring *) bstr__realloc (sl->entry, nsz); + if (!l) return BSTR_ERR; + } + sl->mlen = smsz; + sl->entry = l; + return BSTR_OK; +} + +/* int bstrListAllocMin (struct bstrList * sl, int msz) + * + * Try to allocate the minimum amount of memory for the list to include at + * least msz entries or sl->qty whichever is greater. + */ +int bstrListAllocMin (struct bstrList * sl, int msz) { +bstring * l; +size_t nsz; + if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR; + if (msz < sl->qty) msz = sl->qty; + if (sl->mlen == msz) return BSTR_OK; + nsz = ((size_t) msz) * sizeof (bstring); + if (nsz < (size_t) msz) return BSTR_ERR; + l = (bstring *) bstr__realloc (sl->entry, nsz); + if (!l) return BSTR_ERR; + sl->mlen = msz; + sl->entry = l; + return BSTR_OK; +} + +/* int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + * int (* cb) (void * parm, int ofs, int len), void * parm) + * + * Iterate the set of disjoint sequential substrings over str divided by the + * character in splitChar. + * + * Note: Non-destructive modification of str from within the cb function + * while performing this split is not undefined. bsplitcb behaves in + * sequential lock step with calls to cb. I.e., after returning from a cb + * that return a non-negative integer, bsplitcb continues from the position + * 1 character after the last detected split character and it will halt + * immediately if the length of str falls below this point. However, if the + * cb function destroys str, then it *must* return with a negative value, + * otherwise bsplitcb will continue in an undefined manner. + */ +int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm) { +int i, p, ret; + + if (cb == NULL || str == NULL || pos < 0 || pos > str->slen) + return BSTR_ERR; + + p = pos; + do { + for (i=p; i < str->slen; i++) { + if (str->data[i] == splitChar) break; + } + if ((ret = cb (parm, p, i - p)) < 0) return ret; + p = i + 1; + } while (p <= str->slen); + return BSTR_OK; +} + +/* int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + * int (* cb) (void * parm, int ofs, int len), void * parm) + * + * Iterate the set of disjoint sequential substrings over str divided by any + * of the characters in splitStr. An empty splitStr causes the whole str to + * be iterated once. + * + * Note: Non-destructive modification of str from within the cb function + * while performing this split is not undefined. bsplitscb behaves in + * sequential lock step with calls to cb. I.e., after returning from a cb + * that return a non-negative integer, bsplitscb continues from the position + * 1 character after the last detected split character and it will halt + * immediately if the length of str falls below this point. However, if the + * cb function destroys str, then it *must* return with a negative value, + * otherwise bsplitscb will continue in an undefined manner. + */ +int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm) { +struct charField chrs; +int i, p, ret; + + if (cb == NULL || str == NULL || pos < 0 || pos > str->slen + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + if (splitStr->slen == 0) { + if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0; + return ret; + } + + if (splitStr->slen == 1) + return bsplitcb (str, splitStr->data[0], pos, cb, parm); + + buildCharField (&chrs, splitStr); + + p = pos; + do { + for (i=p; i < str->slen; i++) { + if (testInCharField (&chrs, str->data[i])) break; + } + if ((ret = cb (parm, p, i - p)) < 0) return ret; + p = i + 1; + } while (p <= str->slen); + return BSTR_OK; +} + +/* int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + * int (* cb) (void * parm, int ofs, int len), void * parm) + * + * Iterate the set of disjoint sequential substrings over str divided by the + * substring splitStr. An empty splitStr causes the whole str to be + * iterated once. + * + * Note: Non-destructive modification of str from within the cb function + * while performing this split is not undefined. bsplitstrcb behaves in + * sequential lock step with calls to cb. I.e., after returning from a cb + * that return a non-negative integer, bsplitscb continues from the position + * 1 character after the last detected split character and it will halt + * immediately if the length of str falls below this point. However, if the + * cb function destroys str, then it *must* return with a negative value, + * otherwise bsplitscb will continue in an undefined manner. + */ +int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm) { +int i, p, ret; + + if (cb == NULL || str == NULL || pos < 0 || pos > str->slen + || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; + + if (0 == splitStr->slen) { + for (i=pos; i < str->slen; i++) { + if ((ret = cb (parm, i, 1)) < 0) return ret; + } + return BSTR_OK; + } + + if (splitStr->slen == 1) + return bsplitcb (str, splitStr->data[0], pos, cb, parm); + + for (i=p=pos; i <= str->slen - splitStr->slen; i++) { + if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) { + if ((ret = cb (parm, p, i - p)) < 0) return ret; + i += splitStr->slen; + p = i; + } + } + if ((ret = cb (parm, p, str->slen - p)) < 0) return ret; + return BSTR_OK; +} + +struct genBstrList { + bstring b; + struct bstrList * bl; +}; + +static int bscb (void * parm, int ofs, int len) { +struct genBstrList * g = (struct genBstrList *) parm; + if (g->bl->qty >= g->bl->mlen) { + int mlen = g->bl->mlen * 2; + bstring * tbl; + + while (g->bl->qty >= mlen) { + if (mlen < g->bl->mlen) return BSTR_ERR; + mlen += mlen; + } + + tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen); + if (tbl == NULL) return BSTR_ERR; + + g->bl->entry = tbl; + g->bl->mlen = mlen; + } + + g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len); + g->bl->qty++; + return BSTR_OK; +} + +/* struct bstrList * bsplit (const_bstring str, unsigned char splitChar) + * + * Create an array of sequential substrings from str divided by the character + * splitChar. + */ +struct bstrList * bsplit (const_bstring str, unsigned char splitChar) { +struct genBstrList g; + + if (str == NULL || str->data == NULL || str->slen < 0) return NULL; + + g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (g.bl == NULL) return NULL; + g.bl->mlen = 4; + g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); + if (NULL == g.bl->entry) { + bstr__free (g.bl); + return NULL; + } + + g.b = (bstring) str; + g.bl->qty = 0; + if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) { + bstrListDestroy (g.bl); + return NULL; + } + return g.bl; +} + +/* struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) + * + * Create an array of sequential substrings from str divided by the entire + * substring splitStr. + */ +struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) { +struct genBstrList g; + + if (str == NULL || str->data == NULL || str->slen < 0) return NULL; + + g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (g.bl == NULL) return NULL; + g.bl->mlen = 4; + g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); + if (NULL == g.bl->entry) { + bstr__free (g.bl); + return NULL; + } + + g.b = (bstring) str; + g.bl->qty = 0; + if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) { + bstrListDestroy (g.bl); + return NULL; + } + return g.bl; +} + +/* struct bstrList * bsplits (const_bstring str, bstring splitStr) + * + * Create an array of sequential substrings from str divided by any of the + * characters in splitStr. An empty splitStr causes a single entry bstrList + * containing a copy of str to be returned. + */ +struct bstrList * bsplits (const_bstring str, const_bstring splitStr) { +struct genBstrList g; + + if ( str == NULL || str->slen < 0 || str->data == NULL || + splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL) + return NULL; + + g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); + if (g.bl == NULL) return NULL; + g.bl->mlen = 4; + g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); + if (NULL == g.bl->entry) { + bstr__free (g.bl); + return NULL; + } + g.b = (bstring) str; + g.bl->qty = 0; + + if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) { + bstrListDestroy (g.bl); + return NULL; + } + return g.bl; +} + +#if defined (__TURBOC__) && !defined (__BORLANDC__) +# ifndef BSTRLIB_NOVSNP +# define BSTRLIB_NOVSNP +# endif +#endif + +/* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */ +#if defined(__WATCOMC__) || defined(_MSC_VER) +#define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);} +#else +#ifdef BSTRLIB_NOVSNP +/* This is just a hack. If you are using a system without a vsnprintf, it is + not recommended that bformat be used at all. */ +#define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;} +#define START_VSNBUFF (256) +#else + +#ifdef __GNUC__ +/* Something is making gcc complain about this prototype not being here, so + I've just gone ahead and put it in. */ +//extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg); +#endif + +#define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);} +#endif +#endif + +#if !defined (BSTRLIB_NOVSNP) + +#ifndef START_VSNBUFF +#define START_VSNBUFF (16) +#endif + +/* On IRIX vsnprintf returns n-1 when the operation would overflow the target + buffer, WATCOM and MSVC both return -1, while C99 requires that the + returned value be exactly what the length would be if the buffer would be + large enough. This leads to the idea that if the return value is larger + than n, then changing n to the return value will reduce the number of + iterations required. */ + +/* int bformata (bstring b, const char * fmt, ...) + * + * After the first parameter, it takes the same parameters as printf (), but + * rather than outputting results to stdio, it appends the results to + * a bstring which contains what would have been output. Note that if there + * is an early generation of a '\0' character, the bstring will be truncated + * to this end point. + */ +int bformata (bstring b, const char * fmt, ...) { +va_list arglist; +bstring buff; +int n, r; + + if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 + || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "vsnprintf" call (to avoid buffer + overflows) on increasing potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) { + n = 1; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR; + } + + for (;;) { + va_start (arglist, fmt); + exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); + va_end (arglist); + + buff->data[n] = (unsigned char) '\0'; + buff->slen = (int) (strlen) ((char *) buff->data); + + if (buff->slen < n) break; + + if (r > n) n = r; else n += n; + + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return BSTR_ERR; + } + } + + r = bconcat (b, buff); + bdestroy (buff); + return r; +} + +/* int bassignformat (bstring b, const char * fmt, ...) + * + * After the first parameter, it takes the same parameters as printf (), but + * rather than outputting results to stdio, it outputs the results to + * the bstring parameter b. Note that if there is an early generation of a + * '\0' character, the bstring will be truncated to this end point. + */ +int bassignformat (bstring b, const char * fmt, ...) { +va_list arglist; +bstring buff; +int n, r; + + if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 + || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "vsnprintf" call (to avoid buffer + overflows) on increasing potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) { + n = 1; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR; + } + + for (;;) { + va_start (arglist, fmt); + exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); + va_end (arglist); + + buff->data[n] = (unsigned char) '\0'; + buff->slen = (int) (strlen) ((char *) buff->data); + + if (buff->slen < n) break; + + if (r > n) n = r; else n += n; + + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return BSTR_ERR; + } + } + + r = bassign (b, buff); + bdestroy (buff); + return r; +} + +/* bstring bformat (const char * fmt, ...) + * + * Takes the same parameters as printf (), but rather than outputting results + * to stdio, it forms a bstring which contains what would have been output. + * Note that if there is an early generation of a '\0' character, the + * bstring will be truncated to this end point. + */ +bstring bformat (const char * fmt, ...) { +va_list arglist; +bstring buff; +int n, r; + + if (fmt == NULL) return NULL; + + /* Since the length is not determinable beforehand, a search is + performed using the truncating "vsnprintf" call (to avoid buffer + overflows) on increasing potential sizes for the output result. */ + + if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) { + n = 1; + if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL; + } + + for (;;) { + va_start (arglist, fmt); + exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); + va_end (arglist); + + buff->data[n] = (unsigned char) '\0'; + buff->slen = (int) (strlen) ((char *) buff->data); + + if (buff->slen < n) break; + + if (r > n) n = r; else n += n; + + if (BSTR_OK != balloc (buff, n + 2)) { + bdestroy (buff); + return NULL; + } + } + + return buff; +} + +/* int bvcformata (bstring b, int count, const char * fmt, va_list arglist) + * + * The bvcformata function formats data under control of the format control + * string fmt and attempts to append the result to b. The fmt parameter is + * the same as that of the printf function. The variable argument list is + * replaced with arglist, which has been initialized by the va_start macro. + * The size of the appended output is upper bounded by count. If the + * required output exceeds count, the string b is not augmented with any + * contents and a value below BSTR_ERR is returned. If a value below -count + * is returned then it is recommended that the negative of this value be + * used as an update to the count in a subsequent pass. On other errors, + * such as running out of memory, parameter errors or numeric wrap around + * BSTR_ERR is returned. BSTR_OK is returned when the output is successfully + * generated and appended to b. + * + * Note: There is no sanity checking of arglist, and this function is + * destructive of the contents of b from the b->slen point onward. If there + * is an early generation of a '\0' character, the bstring will be truncated + * to this end point. + */ +int bvcformata (bstring b, int count, const char * fmt, va_list arg) { +int n, r, l; + + if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL + || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; + + if (count > (n = b->slen + count) + 2) return BSTR_ERR; + if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR; + + exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg); + + /* Did the operation complete successfully within bounds? */ + for (l = b->slen; l <= n; l++) { + if ('\0' == b->data[l]) { + b->slen = l; + return BSTR_OK; + } + } + + /* Abort, since the buffer was not large enough. The return value + tries to help set what the retry length should be. */ + + b->data[b->slen] = '\0'; + if (r > count + 1) { /* Does r specify a particular target length? */ + n = r; + } else { + n = count + count; /* If not, just double the size of count */ + if (count > n) n = INT_MAX; + } + n = -n; + + if (n > BSTR_ERR-1) n = BSTR_ERR-1; + return n; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.h b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.h new file mode 100644 index 0000000000..edf8c00fc6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.h @@ -0,0 +1,305 @@ +/* + * This source file is part of the bstring string library. This code was + * written by Paul Hsieh in 2002-2010, and is covered by either the 3-clause + * BSD open source license or GPL v2.0. Refer to the accompanying documentation + * for details on usage and license. + */ +// Modifications copyright Amazon.com, Inc. or its affiliates + +/* + * bstrlib.h + * + * This file is the header file for the core module for implementing the + * bstring functions. + */ + +#ifndef BSTRLIB_INCLUDE +#define BSTRLIB_INCLUDE + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP) +# if defined (__TURBOC__) && !defined (__BORLANDC__) +# define BSTRLIB_NOVSNP +# endif +#endif + +#define BSTR_ERR (-1) +#define BSTR_OK (0) +#define BSTR_BS_BUFF_LENGTH_GET (0) + +typedef struct tagbstring * bstring; +typedef const struct tagbstring * const_bstring; + +/* Copy functions */ +#define cstr2bstr bfromcstr +extern bstring bfromcstr (const char * str); +extern bstring bfromcstralloc (int mlen, const char * str); +extern bstring blk2bstr (const void * blk, int len); +extern char * bstr2cstr (const_bstring s, char z); +extern int bcstrfree (char * s); +extern bstring bstrcpy (const_bstring b1); +extern int bassign (bstring a, const_bstring b); +extern int bassignmidstr (bstring a, const_bstring b, int left, int len); +extern int bassigncstr (bstring a, const char * str); +extern int bassignblk (bstring a, const void * s, int len); + +/* Destroy function */ +extern int bdestroy (bstring b); + +/* Space allocation hinting functions */ +extern int balloc (bstring s, int len); +extern int ballocmin (bstring b, int len); + +/* Substring extraction */ +extern bstring bmidstr (const_bstring b, int left, int len); + +/* Various standard manipulations */ +extern int bconcat (bstring b0, const_bstring b1); +extern int bconchar (bstring b0, char c); +extern int bcatcstr (bstring b, const char * s); +extern int bcatblk (bstring b, const void * s, int len); +extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill); +extern int binsertch (bstring s1, int pos, int len, unsigned char fill); +extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill); +extern int bdelete (bstring s1, int pos, int len); +extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill); +extern int btrunc (bstring b, int n); + +/* Scan/search functions */ +extern int bstricmp (const_bstring b0, const_bstring b1); +extern int bstrnicmp (const_bstring b0, const_bstring b1, int n); +extern int biseqcaseless (const_bstring b0, const_bstring b1); +extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len); +extern int biseq (const_bstring b0, const_bstring b1); +extern int bisstemeqblk (const_bstring b0, const void * blk, int len); +extern int biseqcstr (const_bstring b, const char * s); +extern int biseqcstrcaseless (const_bstring b, const char * s); +extern int bstrcmp (const_bstring b0, const_bstring b1); +extern int bstrncmp (const_bstring b0, const_bstring b1, int n); +extern int binstr (const_bstring s1, int pos, const_bstring s2); +extern int binstrr (const_bstring s1, int pos, const_bstring s2); +extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2); +extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2); +extern int bstrchrp (const_bstring b, int c, int pos); +extern int bstrrchrp (const_bstring b, int c, int pos); +#define bstrchr(b,c) bstrchrp ((b), (c), 0) +#define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1) +extern int binchr (const_bstring b0, int pos, const_bstring b1); +extern int binchrr (const_bstring b0, int pos, const_bstring b1); +extern int bninchr (const_bstring b0, int pos, const_bstring b1); +extern int bninchrr (const_bstring b0, int pos, const_bstring b1); +extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos); +extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos); + +/* List of string container functions */ +struct bstrList { + int qty, mlen; + bstring * entry; +}; +extern struct bstrList * bstrListCreate (void); +extern int bstrListDestroy (struct bstrList * sl); +extern int bstrListAlloc (struct bstrList * sl, int msz); +extern int bstrListAllocMin (struct bstrList * sl, int msz); + +/* String split and join functions */ +extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar); +extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr); +extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr); +extern bstring bjoin (const struct bstrList * bl, const_bstring sep); +extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); +extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); +extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + +/* Miscellaneous functions */ +extern int bpattern (bstring b, int len); +extern int btoupper (bstring b); +extern int btolower (bstring b); +extern int bltrimws (bstring b); +extern int brtrimws (bstring b); +extern int btrimws (bstring b); + +/* <*>printf format functions */ +#if !defined (BSTRLIB_NOVSNP) +extern bstring bformat (const char * fmt, ...); +extern int bformata (bstring b, const char * fmt, ...); +extern int bassignformat (bstring b, const char * fmt, ...); +extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist); + +#define bvformata(ret, b, fmt, lastarg) { \ +bstring bstrtmp_b = (b); \ +const char * bstrtmp_fmt = (fmt); \ +int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \ + for (;;) { \ + va_list bstrtmp_arglist; \ + va_start (bstrtmp_arglist, lastarg); \ + bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \ + va_end (bstrtmp_arglist); \ + if (bstrtmp_r >= 0) { /* Everything went ok */ \ + bstrtmp_r = BSTR_OK; \ + break; \ + } else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \ + bstrtmp_r = BSTR_ERR; \ + break; \ + } \ + bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \ + } \ + ret = bstrtmp_r; \ +} + +#endif + +typedef int (*bNgetc) (void *parm); +typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm); + +/* Input functions */ +extern bstring bgets (bNgetc getcPtr, void * parm, char terminator); +extern bstring bread (bNread readPtr, void * parm); +extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator); +extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator); +extern int breada (bstring b, bNread readPtr, void * parm); + +/* Stream functions */ +extern struct bStream * bsopen (bNread readPtr, void * parm); +extern void * bsclose (struct bStream * s); +extern int bsbufflength (struct bStream * s, int sz); +extern int bsreadln (bstring b, struct bStream * s, char terminator); +extern int bsreadlns (bstring r, struct bStream * s, const_bstring term); +extern int bsread (bstring b, struct bStream * s, int n); +extern int bsreadlna (bstring b, struct bStream * s, char terminator); +extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term); +extern int bsreada (bstring b, struct bStream * s, int n); +extern int bsunread (struct bStream * s, const_bstring b); +extern int bspeek (bstring r, const struct bStream * s); +extern int bssplitscb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); +extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); +extern int bseof (const struct bStream * s); + +struct tagbstring { + int mlen; + int slen; + unsigned char * data; +}; + +/* Accessor macros */ +#define blengthe(b, e) (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen)) +#define blength(b) (blengthe ((b), 0)) +#define bdataofse(b, o, e) (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o)) +#define bdataofs(b, o) (bdataofse ((b), (o), (void *)0)) +#define bdatae(b, e) (bdataofse (b, 0, e)) +#define bdata(b) (bdataofs (b, 0)) +#define bchare(b, p, e) ((((unsigned)(p)) < (unsigned)blength(b)) ? ((b)->data[(p)]) : (e)) +#define bchar(b, p) bchare ((b), (p), '\0') + +/* Static constant string initialization macro */ +#define bsStaticMlen(q,m) {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")} +#if defined(_MSC_VER) +/* There are many versions of MSVC which emit __LINE__ as a non-constant. */ +# define bsStatic(q) bsStaticMlen(q,-32) +#endif +#ifndef bsStatic +# define bsStatic(q) bsStaticMlen(q,-__LINE__) +#endif + +/* Static constant block parameter pair */ +#define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1) + +/* Reference building macros */ +#define cstr2tbstr btfromcstr +#define btfromcstr(t,s) { \ + (t).data = (unsigned char *) (s); \ + (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \ + (t).mlen = -1; \ +} +#define blk2tbstr(t,s,l) { \ + (t).data = (unsigned char *) (s); \ + (t).slen = l; \ + (t).mlen = -1; \ +} +#define btfromblk(t,s,l) blk2tbstr(t,s,l) +#define bmid2tbstr(t,b,p,l) { \ + const_bstring bstrtmp_s = (b); \ + if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) { \ + int bstrtmp_left = (p); \ + int bstrtmp_len = (l); \ + if (bstrtmp_left < 0) { \ + bstrtmp_len += bstrtmp_left; \ + bstrtmp_left = 0; \ + } \ + if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left) \ + bstrtmp_len = bstrtmp_s->slen - bstrtmp_left; \ + if (bstrtmp_len <= 0) { \ + (t).data = (unsigned char *)""; \ + (t).slen = 0; \ + } else { \ + (t).data = bstrtmp_s->data + bstrtmp_left; \ + (t).slen = bstrtmp_len; \ + } \ + } else { \ + (t).data = (unsigned char *)""; \ + (t).slen = 0; \ + } \ + (t).mlen = -__LINE__; \ +} +#define btfromblkltrimws(t,s,l) { \ + int bstrtmp_idx = 0, bstrtmp_len = (l); \ + unsigned char * bstrtmp_s = (s); \ + if (bstrtmp_s && bstrtmp_len >= 0) { \ + for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) { \ + if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \ + } \ + } \ + (t).data = bstrtmp_s + bstrtmp_idx; \ + (t).slen = bstrtmp_len - bstrtmp_idx; \ + (t).mlen = -__LINE__; \ +} +#define btfromblkrtrimws(t,s,l) { \ + int bstrtmp_len = (l) - 1; \ + unsigned char * bstrtmp_s = (s); \ + if (bstrtmp_s && bstrtmp_len >= 0) { \ + for (; bstrtmp_len >= 0; bstrtmp_len--) { \ + if (!isspace (bstrtmp_s[bstrtmp_len])) break; \ + } \ + } \ + (t).data = bstrtmp_s; \ + (t).slen = bstrtmp_len + 1; \ + (t).mlen = -__LINE__; \ +} +#define btfromblktrimws(t,s,l) { \ + int bstrtmp_idx = 0, bstrtmp_len = (l) - 1; \ + unsigned char * bstrtmp_s = (s); \ + if (bstrtmp_s && bstrtmp_len >= 0) { \ + for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) { \ + if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \ + } \ + for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) { \ + if (!isspace (bstrtmp_s[bstrtmp_len])) break; \ + } \ + } \ + (t).data = bstrtmp_s + bstrtmp_idx; \ + (t).slen = bstrtmp_len + 1 - bstrtmp_idx; \ + (t).mlen = -__LINE__; \ +} + +/* Write protection macros */ +#define bwriteprotect(t) { if ((t).mlen >= 0) (t).mlen = -1; } +#define bwriteallow(t) { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); } +#define biswriteprotected(t) ((t).mlen <= 0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.txt b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.txt new file mode 100644 index 0000000000..8ebb188853 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/bstrlib.txt @@ -0,0 +1,3201 @@ +Better String library +--------------------- + +by Paul Hsieh + +The bstring library is an attempt to provide improved string processing +functionality to the C and C++ language. At the heart of the bstring library +(Bstrlib for short) is the management of "bstring"s which are a significant +improvement over '\0' terminated char buffers. + +=============================================================================== + +Motivation +---------- + +The standard C string library has serious problems: + + 1) Its use of '\0' to denote the end of the string means knowing a + string's length is O(n) when it could be O(1). + 2) It imposes an interpretation for the character value '\0'. + 3) gets() always exposes the application to a buffer overflow. + 4) strtok() modifies the string its parsing and thus may not be usable in + programs which are re-entrant or multithreaded. + 5) fgets has the unusual semantic of ignoring '\0's that occur before + '\n's are consumed. + 6) There is no memory management, and actions performed such as strcpy, + strcat and sprintf are common places for buffer overflows. + 7) strncpy() doesn't '\0' terminate the destination in some cases. + 8) Passing NULL to C library string functions causes an undefined NULL + pointer access. + 9) Parameter aliasing (overlapping, or self-referencing parameters) + within most C library functions has undefined behavior. + 10) Many C library string function calls take integer parameters with + restricted legal ranges. Parameters passed outside these ranges are + not typically detected and cause undefined behavior. + +So the desire is to create an alternative string library that does not suffer +from the above problems and adds in the following functionality: + + 1) Incorporate string functionality seen from other languages. + a) MID$() - from BASIC + b) split()/join() - from Python + c) string/char x n - from Perl + 2) Implement analogs to functions that combine stream IO and char buffers + without creating a dependency on stream IO functionality. + 3) Implement the basic text editor-style functions insert, delete, find, + and replace. + 4) Implement reference based sub-string access (as a generalization of + pointer arithmetic.) + 5) Implement runtime write protection for strings. + +There is also a desire to avoid "API-bloat". So functionality that can be +implemented trivially in other functionality is omitted. So there is no +left$() or right$() or reverse() or anything like that as part of the core +functionality. + +Explaining Bstrings +------------------- + +A bstring is basically a header which wraps a pointer to a char buffer. Lets +start with the declaration of a struct tagbstring: + + struct tagbstring { + int mlen; + int slen; + unsigned char * data; + }; + +This definition is considered exposed, not opaque (though it is neither +necessary nor recommended that low level maintenance of bstrings be performed +whenever the abstract interfaces are sufficient). The mlen field (usually) +describes a lower bound for the memory allocated for the data field. The +slen field describes the exact length for the bstring. The data field is a +single contiguous buffer of unsigned chars. Note that the existence of a '\0' +character in the unsigned char buffer pointed to by the data field does not +necessarily denote the end of the bstring. + +To be a well formed modifiable bstring the mlen field must be at least the +length of the slen field, and slen must be non-negative. Furthermore, the +data field must point to a valid buffer in which access to the first mlen +characters has been acquired. So the minimal check for correctness is: + + (slen >= 0 && mlen >= slen && data != NULL) + +bstrings returned by bstring functions can be assumed to be either NULL or +satisfy the above property. (When bstrings are only readable, the mlen >= +slen restriction is not required; this is discussed later in this section.) +A bstring itself is just a pointer to a struct tagbstring: + + typedef struct tagbstring * bstring; + +Note that use of the prefix "tag" in struct tagbstring is required to work +around the inconsistency between C and C++'s struct namespace usage. This +definition is also considered exposed. + +Bstrlib basically manages bstrings allocated as a header and an associated +data-buffer. Since the implementation is exposed, they can also be +constructed manually. Functions which mutate bstrings assume that the header +and data buffer have been malloced; the bstring library may perform free() or +realloc() on both the header and data buffer of any bstring parameter. +Functions which return bstring's create new bstrings. The string memory is +freed by a bdestroy() call (or using the bstrFree macro). + +The following related typedef is also provided: + + typedef const struct tagbstring * const_bstring; + +which is also considered exposed. These are directly bstring compatible (no +casting required) but are just used for parameters which are meant to be +non-mutable. So in general, bstring parameters which are read as input but +not meant to be modified will be declared as const_bstring, and bstring +parameters which may be modified will be declared as bstring. This convention +is recommended for user written functions as well. + +Since bstrings maintain interoperability with C library char-buffer style +strings, all functions which modify, update or create bstrings also append a +'\0' character into the position slen + 1. This trailing '\0' character is +not required for bstrings input to the bstring functions; this is provided +solely as a convenience for interoperability with standard C char-buffer +functionality. + +Analogs for the ANSI C string library functions have been created when they +are necessary, but have also been left out when they are not. In particular +there are no functions analogous to fwrite, or puts just for the purposes of +bstring. The ->data member of any string is exposed, and therefore can be +used just as easily as char buffers for C functions which read strings. + +For those that wish to hand construct bstrings, the following should be kept +in mind: + + 1) While bstrlib can accept constructed bstrings without terminating + '\0' characters, the rest of the C language string library will not + function properly on such non-terminated strings. This is obvious + but must be kept in mind. + 2) If it is intended that a constructed bstring be written to by the + bstring library functions then the data portion should be allocated + by the malloc function and the slen and mlen fields should be entered + properly. The struct tagbstring header is not reallocated, and only + freed by bdestroy. + 3) Writing arbitrary '\0' characters at various places in the string + will not modify its length as perceived by the bstring library + functions. In fact, '\0' is a legitimate non-terminating character + for a bstring to contain. + 4) For read only parameters, bstring functions do not check the mlen. + I.e., the minimal correctness requirements are reduced to: + + (slen >= 0 && data != NULL) + +Better pointer arithmetic +------------------------- + +One built-in feature of '\0' terminated char * strings, is that its very easy +and fast to obtain a reference to the tail of any string using pointer +arithmetic. Bstrlib does one better by providing a way to get a reference to +any substring of a bstring (or any other length delimited block of memory.) +So rather than just having pointer arithmetic, with bstrlib one essentially +has segment arithmetic. This is achieved using the macro blk2tbstr() which +builds a reference to a block of memory and the macro bmid2tbstr() which +builds a reference to a segment of a bstring. Bstrlib also includes +functions for direct consumption of memory blocks into bstrings, namely +bcatblk () and blk2bstr (). + +One scenario where this can be extremely useful is when string contains many +substrings which one would like to pass as read-only reference parameters to +some string consuming function without the need to allocate entire new +containers for the string data. More concretely, imagine parsing a command +line string whose parameters are space delimited. This can only be done for +tails of the string with '\0' terminated char * strings. + +Improved NULL semantics and error handling +------------------------------------------ + +Unless otherwise noted, if a NULL pointer is passed as a bstring or any other +detectably illegal parameter, the called function will return with an error +indicator (either NULL or BSTR_ERR) rather than simply performing a NULL +pointer access, or having undefined behavior. + +To illustrate the value of this, consider the following example: + + strcpy (p = malloc (13 * sizeof (char)), "Hello,"); + strcat (p, " World"); + +This is not correct because malloc may return NULL (due to an out of memory +condition), and the behaviour of strcpy is undefined if either of its +parameters are NULL. However: + + bstrcat (p = bfromcstr ("Hello,"), q = bfromcstr (" World")); + bdestroy (q); + +is well defined, because if either p or q are assigned NULL (indicating a +failure to allocate memory) both bstrcat and bdestroy will recognize it and +perform no detrimental action. + +Note that it is not necessary to check any of the members of a returned +bstring for internal correctness (in particular the data member does not need +to be checked against NULL when the header is non-NULL), since this is +assured by the bstring library itself. + +bStreams +-------- + +In addition to the bgets and bread functions, bstrlib can abstract streams +with a high performance read only stream called a bStream. In general, the +idea is to open a core stream (with something like fopen) then pass its +handle as well as a bNread function pointer (like fread) to the bsopen +function which will return a handle to an open bStream. Then the functions +bsread, bsreadln or bsreadlns can be called to read portions of the stream. +Finally, the bsclose function is called to close the bStream -- it will +return a handle to the original (core) stream. So bStreams, essentially, +wrap other streams. + +The bStreams have two main advantages over the bgets and bread (as well as +fgets/ungetc) paradigms: + +1) Improved functionality via the bunread function which allows a stream to + unread characters, giving the bStream stack-like functionality if so + desired. +2) A very high performance bsreadln function. The C library function fgets() + (and the bgets function) can typically be written as a loop on top of + fgetc(), thus paying all of the overhead costs of calling fgetc on a per + character basis. bsreadln will read blocks at a time, thus amortizing the + overhead of fread calls over many characters at once. + +However, clearly bStreams are suboptimal or unusable for certain kinds of +streams (stdin) or certain usage patterns (a few spotty, or non-sequential +reads from a slow stream.) For those situations, using bgets will be more +appropriate. + +The semantics of bStreams allows practical construction of layerable data +streams. What this means is that by writing a bNread compatible function on +top of a bStream, one can construct a new bStream on top of it. This can be +useful for writing multi-pass parsers that don't actually read the entire +input more than once and don't require the use of intermediate storage. + +Aliasing +-------- + +Aliasing occurs when a function is given two parameters which point to data +structures which overlap in the memory they occupy. While this does not +disturb read only functions, for many libraries this can make functions that +write to these memory locations malfunction. This is a common problem of the +C standard library and especially the string functions in the C standard +library. + +The C standard string library is entirely char by char oriented (as is +bstring) which makes conforming implementations alias safe for some +scenarios. However no actual detection of aliasing is typically performed, +so it is easy to find cases where the aliasing will cause anomolous or +undesirable behaviour (consider: strcat (p, p).) The C99 standard includes +the "restrict" pointer modifier which allows the compiler to document and +assume a no-alias condition on usage. However, only the most trivial cases +can be caught (if at all) by the compiler at compile time, and thus there is +no actual enforcement of non-aliasing. + +Bstrlib, by contrast, permits aliasing and is completely aliasing safe, in +the C99 sense of aliasing. That is to say, under the assumption that +pointers of incompatible types from distinct objects can never alias, bstrlib +is completely aliasing safe. (In practice this means that the data buffer +portion of any bstring and header of any bstring are assumed to never alias.) +With the exception of the reference building macros, the library behaves as +if all read-only parameters are first copied and replaced by temporary +non-aliased parameters before any writing to any output bstring is performed +(though actual copying is extremely rarely ever done.) + +Besides being a useful safety feature, bstring searching/comparison +functions can improve to O(1) execution when aliasing is detected. + +Note that aliasing detection and handling code in Bstrlib is generally +extremely cheap. There is almost never any appreciable performance penalty +for using aliased parameters. + +Reenterancy +----------- + +Nearly every function in Bstrlib is a leaf function, and is completely +reenterable with the exception of writing to common bstrings. The split +functions which use a callback mechanism requires only that the source string +not be destroyed by the callback function unless the callback function returns +with an error status (note that Bstrlib functions which return an error do +not modify the string in any way.) The string can in fact be modified by the +callback and the behaviour is deterministic. See the documentation of the +various split functions for more details. + +Undefined scenarios +------------------- + +One of the basic important premises for Bstrlib is to not to increase the +propogation of undefined situations from parameters that are otherwise legal +in of themselves. In particular, except for extremely marginal cases, usages +of bstrings that use the bstring library functions alone cannot lead to any +undefined action. But due to C/C++ language and library limitations, there +is no way to define a non-trivial library that is completely without +undefined operations. All such possible undefined operations are described +below: + +1) bstrings or struct tagbstrings that are not explicitely initialized cannot + be passed as a parameter to any bstring function. +2) The members of the NULL bstring cannot be accessed directly. (Though all + APIs and macros detect the NULL bstring.) +3) A bstring whose data member has not been obtained from a malloc or + compatible call and which is write accessible passed as a writable + parameter will lead to undefined results. (i.e., do not writeAllow any + constructed bstrings unless the data portion has been obtained from the + heap.) +4) If the headers of two strings alias but are not identical (which can only + happen via a defective manual construction), then passing them to a + bstring function in which one is writable is not defined. +5) If the mlen member is larger than the actual accessible length of the data + member for a writable bstring, or if the slen member is larger than the + readable length of the data member for a readable bstring, then the + corresponding bstring operations are undefined. +6) Any bstring definition whose header or accessible data portion has been + assigned to inaccessible or otherwise illegal memory clearly cannot be + acted upon by the bstring library in any way. +7) Destroying the source of an incremental split from within the callback + and not returning with a negative value (indicating that it should abort) + will lead to undefined behaviour. (Though *modifying* or adjusting the + state of the source data, even if those modification fail within the + bstrlib API, has well defined behavior.) +8) Modifying a bstring which is write protected by direct access has + undefined behavior. + +While this may seem like a long list, with the exception of invalid uses of +the writeAllow macro, and source destruction during an iterative split +without an accompanying abort, no usage of the bstring API alone can cause +any undefined scenario to occurr. I.e., the policy of restricting usage of +bstrings to the bstring API can significantly reduce the risk of runtime +errors (in practice it should eliminate them) related to string manipulation +due to undefined action. + +C++ wrapper +----------- + +A C++ wrapper has been created to enable bstring functionality for C++ in the +most natural (for C++ programers) way possible. The mandate for the C++ +wrapper is different from the base C bstring library. Since the C++ language +has far more abstracting capabilities, the CBString structure is considered +fully abstracted -- i.e., hand generated CBStrings are not supported (though +conversion from a struct tagbstring is allowed) and all detectable errors are +manifest as thrown exceptions. + +- The C++ class definitions are all under the namespace Bstrlib. bstrwrap.h + enables this namespace (with a using namespace Bstrlib; directive at the + end) unless the macro BSTRLIB_DONT_ASSUME_NAMESPACE has been defined before + it is included. + +- Erroneous accesses results in an exception being thrown. The exception + parameter is of type "struct CBStringException" which is derived from + std::exception if STL is used. A verbose description of the error message + can be obtained from the what() method. + +- CBString is a C++ structure derived from a struct tagbstring. An address + of a CBString cast to a bstring must not be passed to bdestroy. The bstring + C API has been made C++ safe and can be used directly in a C++ project. + +- It includes constructors which can take a char, '\0' terminated char + buffer, tagbstring, (char, repeat-value), a length delimited buffer or a + CBStringList to initialize it. + +- Concatenation is performed with the + and += operators. Comparisons are + done with the ==, !=, <, >, <= and >= operators. Note that == and != use + the biseq call, while <, >, <= and >= use bstrcmp. + +- CBString's can be directly cast to const character buffers. + +- CBString's can be directly cast to double, float, int or unsigned int so + long as the CBString are decimal representations of those types (otherwise + an exception will be thrown). Converting the other way should be done with + the format(a) method(s). + +- CBString contains the length, character and [] accessor methods. The + character and [] accessors are aliases of each other. If the bounds for + the string are exceeded, an exception is thrown. To avoid the overhead for + this check, first cast the CBString to a (const char *) and use [] to + dereference the array as normal. Note that the character and [] accessor + methods allows both reading and writing of individual characters. + +- The methods: format, formata, find, reversefind, findcaseless, + reversefindcaseless, midstr, insert, insertchrs, replace, findreplace, + findreplacecaseless, remove, findchr, nfindchr, alloc, toupper, tolower, + gets, read are analogous to the functions that can be found in the C API. + +- The caselessEqual and caselessCmp methods are analogous to biseqcaseless + and bstricmp functions respectively. + +- Note that just like the bformat function, the format and formata methods do + not automatically cast CBStrings into char * strings for "%s"-type + substitutions: + + CBString w("world"); + CBString h("Hello"); + CBString hw; + + /* The casts are necessary */ + hw.format ("%s, %s", (const char *)h, (const char *)w); + +- The methods trunc and repeat have been added instead of using pattern. + +- ltrim, rtrim and trim methods have been added. These remove characters + from a given character string set (defaulting to the whitespace characters) + from either the left, right or both ends of the CBString, respectively. + +- The method setsubstr is also analogous in functionality to bsetstr, except + that it cannot be passed NULL. Instead the method fill and the fill-style + constructor have been supplied to enable this functionality. + +- The writeprotect(), writeallow() and iswriteprotected() methods are + analogous to the bwriteprotect(), bwriteallow() and biswriteprotected() + macros in the C API. Write protection semantics in CBString are stronger + than with the C API in that indexed character assignment is checked for + write protection. However, unlike with the C API, a write protected + CBString can be destroyed by the destructor. + +- CBStream is a C++ structure which wraps a struct bStream (its not derived + from it, since destruction is slightly different). It is constructed by + passing in a bNread function pointer and a stream parameter cast to void *. + This structure includes methods for detecting eof, setting the buffer + length, reading the whole stream or reading entries line by line or block + by block, an unread function, and a peek function. + +- If STL is available, the CBStringList structure is derived from a vector of + CBString with various split methods. The split method has been overloaded + to accept either a character or CBString as the second parameter (when the + split parameter is a CBString any character in that CBString is used as a + seperator). The splitstr method takes a CBString as a substring seperator. + Joins can be performed via a CBString constructor which takes a + CBStringList as a parameter, or just using the CBString::join() method. + +- If there is proper support for std::iostreams, then the >> and << operators + and the getline() function have been added (with semantics the same as + those for std::string). + +Multithreading +-------------- + +A mutable bstring is kind of analogous to a small (two entry) linked list +allocated by malloc, with all aliasing completely under programmer control. +I.e., manipulation of one bstring will never affect any other distinct +bstring unless explicitely constructed to do so by the programmer via hand +construction or via building a reference. Bstrlib also does not use any +static or global storage, so there are no hidden unremovable race conditions. +Bstrings are also clearly not inherently thread local. So just like +char *'s, bstrings can be passed around from thread to thread and shared and +so on, so long as modifications to a bstring correspond to some kind of +exclusive access lock as should be expected (or if the bstring is read-only, +which can be enforced by bstring write protection) for any sort of shared +object in a multithreaded environment. + +Bsafe module +------------ + +For convenience, a bsafe module has been included. The idea is that if this +module is included, inadvertant usage of the most dangerous C functions will +be overridden and lead to an immediate run time abort. Of course, it should +be emphasized that usage of this module is completely optional. The +intention is essentially to provide an option for creating project safety +rules which can be enforced mechanically rather than socially. This is +useful for larger, or open development projects where its more difficult to +enforce social rules or "coding conventions". + +Problems not solved +------------------- + +Bstrlib is written for the C and C++ languages, which have inherent weaknesses +that cannot be easily solved: + +1. Memory leaks: Forgetting to call bdestroy on a bstring that is about to be + unreferenced, just as forgetting to call free on a heap buffer that is + about to be dereferenced. Though bstrlib itself is leak free. +2. Read before write usage: In C, declaring an auto bstring does not + automatically fill it with legal/valid contents. This problem has been + somewhat mitigated in C++. (The bstrDeclare and bstrFree macros from + bstraux can be used to help mitigate this problem.) + +Other problems not addressed: + +3. Built-in mutex usage to automatically avoid all bstring internal race + conditions in multitasking environments: The problem with trying to + implement such things at this low a level is that it is typically more + efficient to use locks in higher level primitives. There is also no + platform independent way to implement locks or mutexes. +4. Unicode/widecharacter support. + +Note that except for spotty support of wide characters, the default C +standard library does not address any of these problems either. + +Configurable compilation options +-------------------------------- + +All configuration options are meant solely for the purpose of compiler +compatibility. Configuration options are not meant to change the semantics +or capabilities of the library, except where it is unavoidable. + +Since some C++ compilers don't include the Standard Template Library and some +have the options of disabling exception handling, a number of macros can be +used to conditionally compile support for each of this: + +BSTRLIB_CAN_USE_STL + + - defining this will enable the used of the Standard Template Library. + Defining BSTRLIB_CAN_USE_STL overrides the BSTRLIB_CANNOT_USE_STL macro. + +BSTRLIB_CANNOT_USE_STL + + - defining this will disable the use of the Standard Template Library. + Defining BSTRLIB_CAN_USE_STL overrides the BSTRLIB_CANNOT_USE_STL macro. + +BSTRLIB_CAN_USE_IOSTREAM + + - defining this will enable the used of streams from class std. Defining + BSTRLIB_CAN_USE_IOSTREAM overrides the BSTRLIB_CANNOT_USE_IOSTREAM macro. + +BSTRLIB_CANNOT_USE_IOSTREAM + + - defining this will disable the use of streams from class std. Defining + BSTRLIB_CAN_USE_IOSTREAM overrides the BSTRLIB_CANNOT_USE_IOSTREAM macro. + +BSTRLIB_THROWS_EXCEPTIONS + + - defining this will enable the exception handling within bstring. + Defining BSTRLIB_THROWS_EXCEPTIONS overrides the + BSTRLIB_DOESNT_THROWS_EXCEPTIONS macro. + +BSTRLIB_DOESNT_THROW_EXCEPTIONS + + - defining this will disable the exception handling within bstring. + Defining BSTRLIB_THROWS_EXCEPTIONS overrides the + BSTRLIB_DOESNT_THROW_EXCEPTIONS macro. + +Note that these macros must be defined consistently throughout all modules +that use CBStrings including bstrwrap.cpp. + +Some older C compilers do not support functions such as vsnprintf. This is +handled by the following macro variables: + +BSTRLIB_NOVSNP + + - defining this indicates that the compiler does not support vsnprintf. + This will cause bformat and bformata to not be declared. Note that + for some compilers, such as Turbo C, this is set automatically. + Defining BSTRLIB_NOVSNP overrides the BSTRLIB_VSNP_OK macro. + +BSTRLIB_VSNP_OK + + - defining this will disable the autodetection of compilers the do not + support of compilers that do not support vsnprintf. + Defining BSTRLIB_NOVSNP overrides the BSTRLIB_VSNP_OK macro. + +Semantic compilation options +---------------------------- + +Bstrlib comes with very few compilation options for changing the semantics of +of the library. These are described below. + +BSTRLIB_DONT_ASSUME_NAMESPACE + + - Defining this before including bstrwrap.h will disable the automatic + enabling of the Bstrlib namespace for the C++ declarations. + +BSTRLIB_DONT_USE_VIRTUAL_DESTRUCTOR + + - Defining this will make the CBString destructor non-virtual. + +BSTRLIB_MEMORY_DEBUG + + - Defining this will cause the bstrlib modules bstrlib.c and bstrwrap.cpp + to invoke a #include "memdbg.h". memdbg.h has to be supplied by the user. + +Note that these macros must be defined consistently throughout all modules +that use bstrings or CBStrings including bstrlib.c, bstraux.c and +bstrwrap.cpp. + +=============================================================================== + +Files +----- + +bstrlib.c - C implementaion of bstring functions. +bstrlib.h - C header file for bstring functions. +bstraux.c - C example that implements trivial additional functions. +bstraux.h - C header for bstraux.c +bstest.c - C unit/regression test for bstrlib.c + +bstrwrap.cpp - C++ implementation of CBString. +bstrwrap.h - C++ header file for CBString. +test.cpp - C++ unit/regression test for bstrwrap.cpp + +bsafe.c - C runtime stubs to abort usage of unsafe C functions. +bsafe.h - C header file for bsafe.c functions. + +C projects need only include bstrlib.h and compile/link bstrlib.c to use the +bstring library. C++ projects need to additionally include bstrwrap.h and +compile/link bstrwrap.cpp. For both, there may be a need to make choices +about feature configuration as described in the "Configurable compilation +options" in the section above. + +Other files that are included in this archive are: + +license.txt - The 3 clause BSD license for Bstrlib +gpl.txt - The GPL version 2 +security.txt - A security statement useful for auditting Bstrlib +porting.txt - A guide to porting Bstrlib +bstrlib.txt - This file + +=============================================================================== + +The functions +------------- + + extern bstring bfromcstr (const char * str); + + Take a standard C library style '\0' terminated char buffer and generate + a bstring with the same contents as the char buffer. If an error occurs + NULL is returned. + + So for example: + + bstring b = bfromcstr ("Hello"); + if (!b) { + fprintf (stderr, "Out of memory"); + } else { + puts ((char *) b->data); + } + + .......................................................................... + + extern bstring bfromcstralloc (int mlen, const char * str); + + Create a bstring which contains the contents of the '\0' terminated + char * buffer str. The memory buffer backing the bstring is at least + mlen characters in length. If an error occurs NULL is returned. + + So for example: + + bstring b = bfromcstralloc (64, someCstr); + if (b) b->data[63] = 'x'; + + The idea is that this will set the 64th character of b to 'x' if it is at + least 64 characters long otherwise do nothing. And we know this is well + defined so long as b was successfully created, since it will have been + allocated with at least 64 characters. + + .......................................................................... + + extern bstring blk2bstr (const void * blk, int len); + + Create a bstring whose contents are described by the contiguous buffer + pointing to by blk with a length of len bytes. Note that this function + creates a copy of the data in blk, rather than simply referencing it. + Compare with the blk2tbstr macro. If an error occurs NULL is returned. + + .......................................................................... + + extern char * bstr2cstr (const_bstring s, char z); + + Create a '\0' terminated char buffer which contains the contents of the + bstring s, except that any contained '\0' characters are converted to the + character in z. This returned value should be freed with bcstrfree(), by + the caller. If an error occurs NULL is returned. + + .......................................................................... + + extern int bcstrfree (char * s); + + Frees a C-string generated by bstr2cstr (). This is normally unnecessary + since it just wraps a call to free (), however, if malloc () and free () + have been redefined as a macros within the bstrlib module (via macros in + the memdbg.h backdoor) with some difference in behaviour from the std + library functions, then this allows a correct way of freeing the memory + that allows higher level code to be independent from these macro + redefinitions. + + .......................................................................... + + extern bstring bstrcpy (const_bstring b1); + + Make a copy of the passed in bstring. The copied bstring is returned if + there is no error, otherwise NULL is returned. + + .......................................................................... + + extern int bassign (bstring a, const_bstring b); + + Overwrite the bstring a with the contents of bstring b. Note that the + bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a is not overwritten. + + .......................................................................... + + int bassigncstr (bstring a, const char * str); + + Overwrite the string a with the contents of char * string str. Note that + the bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a may be partially overwritten. + + .......................................................................... + + int bassignblk (bstring a, const void * s, int len); + + Overwrite the string a with the contents of the block (s, len). Note that + the bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a is not overwritten. + + .......................................................................... + + extern int bassignmidstr (bstring a, const_bstring b, int left, int len); + + Overwrite the bstring a with the middle of contents of bstring b + starting from position left and running for a length len. left and + len are clamped to the ends of b as with the function bmidstr. Note that + the bstring a must be a well defined and writable bstring. If an error + occurs BSTR_ERR is returned and a is not overwritten. + + .......................................................................... + + extern bstring bmidstr (const_bstring b, int left, int len); + + Create a bstring which is the substring of b starting from position left + and running for a length len (clamped by the end of the bstring b.) If + there was no error, the value of this constructed bstring is returned + otherwise NULL is returned. + + .......................................................................... + + extern int bdelete (bstring s1, int pos, int len); + + Removes characters from pos to pos+len-1 and shifts the tail of the + bstring starting from pos+len to pos. len must be positive for this call + to have any effect. The section of the bstring described by (pos, len) + is clamped to boundaries of the bstring b. The value BSTR_OK is returned + if the operation is successful, otherwise BSTR_ERR is returned. + + .......................................................................... + + extern int bconcat (bstring b0, const_bstring b1); + + Concatenate the bstring b1 to the end of bstring b0. The value BSTR_OK + is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int bconchar (bstring b, char c); + + Concatenate the character c to the end of bstring b. The value BSTR_OK + is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int bcatcstr (bstring b, const char * s); + + Concatenate the char * string s to the end of bstring b. The value + BSTR_OK is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int bcatblk (bstring b, const void * s, int len); + + Concatenate a fixed length buffer (s, len) to the end of bstring b. The + value BSTR_OK is returned if the operation is successful, otherwise + BSTR_ERR is returned. + + .......................................................................... + + extern int biseq (const_bstring b0, const_bstring b1); + + Compare the bstring b0 and b1 for equality. If the bstrings differ, 0 + is returned, if the bstrings are the same, 1 is returned, if there is an + error, -1 is returned. If the length of the bstrings are different, this + function has O(1) complexity. Contained '\0' characters are not treated + as a termination character. + + Note that the semantics of biseq are not completely compatible with + bstrcmp because of its different treatment of the '\0' character. + + .......................................................................... + + extern int bisstemeqblk (const_bstring b, const void * blk, int len); + + Compare beginning of bstring b0 with a block of memory of length len for + equality. If the beginning of b0 differs from the memory block (or if b0 + is too short), 0 is returned, if the bstrings are the same, 1 is returned, + if there is an error, -1 is returned. + + .......................................................................... + + extern int biseqcaseless (const_bstring b0, const_bstring b1); + + Compare two bstrings for equality without differentiating between case. + If the bstrings differ other than in case, 0 is returned, if the bstrings + are the same, 1 is returned, if there is an error, -1 is returned. If + the length of the bstrings are different, this function is O(1). '\0' + termination characters are not treated in any special way. + + .......................................................................... + + extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len); + + Compare beginning of bstring b0 with a block of memory of length len + without differentiating between case for equality. If the beginning of b0 + differs from the memory block other than in case (or if b0 is too short), + 0 is returned, if the bstrings are the same, 1 is returned, if there is an + error, -1 is returned. + + .......................................................................... + + extern int biseqcstr (const_bstring b, const char *s); + + Compare the bstring b and char * bstring s. The C string s must be '\0' + terminated at exactly the length of the bstring b, and the contents + between the two must be identical with the bstring b with no '\0' + characters for the two contents to be considered equal. This is + equivalent to the condition that their current contents will be always be + equal when comparing them in the same format after converting one or the + other. If they are equal 1 is returned, if they are unequal 0 is + returned and if there is a detectable error BSTR_ERR is returned. + + .......................................................................... + + extern int biseqcstrcaseless (const_bstring b, const char *s); + + Compare the bstring b and char * string s. The C string s must be '\0' + terminated at exactly the length of the bstring b, and the contents + between the two must be identical except for case with the bstring b with + no '\0' characters for the two contents to be considered equal. This is + equivalent to the condition that their current contents will be always be + equal ignoring case when comparing them in the same format after + converting one or the other. If they are equal, except for case, 1 is + returned, if they are unequal regardless of case 0 is returned and if + there is a detectable error BSTR_ERR is returned. + + .......................................................................... + + extern int bstrcmp (const_bstring b0, const_bstring b1); + + Compare the bstrings b0 and b1 for ordering. If there is an error, + SHRT_MIN is returned, otherwise a value less than or greater than zero, + indicating that the bstring pointed to by b0 is lexicographically less + than or greater than the bstring pointed to by b1 is returned. If the + bstring lengths are unequal but the characters up until the length of the + shorter are equal then a value less than, or greater than zero, + indicating that the bstring pointed to by b0 is shorter or longer than the + bstring pointed to by b1 is returned. 0 is returned if and only if the + two bstrings are the same. If the length of the bstrings are different, + this function is O(n). Like its standard C library counter part, the + comparison does not proceed past any '\0' termination characters + encountered. + + The seemingly odd error return value, merely provides slightly more + granularity than the undefined situation given in the C library function + strcmp. The function otherwise behaves very much like strcmp(). + + Note that the semantics of bstrcmp are not completely compatible with + biseq because of its different treatment of the '\0' termination + character. + + .......................................................................... + + extern int bstrncmp (const_bstring b0, const_bstring b1, int n); + + Compare the bstrings b0 and b1 for ordering for at most n characters. If + there is an error, SHRT_MIN is returned, otherwise a value is returned as + if b0 and b1 were first truncated to at most n characters then bstrcmp + was called with these new bstrings are paremeters. If the length of the + bstrings are different, this function is O(n). Like its standard C + library counter part, the comparison does not proceed past any '\0' + termination characters encountered. + + The seemingly odd error return value, merely provides slightly more + granularity than the undefined situation given in the C library function + strncmp. The function otherwise behaves very much like strncmp(). + + .......................................................................... + + extern int bstricmp (const_bstring b0, const_bstring b1); + + Compare two bstrings without differentiating between case. The return + value is the difference of the values of the characters where the two + bstrings first differ, otherwise 0 is returned indicating that the + bstrings are equal. If the lengths are different, then a difference from + 0 is given, but if the first extra character is '\0', then it is taken to + be the value UCHAR_MAX+1. + + .......................................................................... + + extern int bstrnicmp (const_bstring b0, const_bstring b1, int n); + + Compare two bstrings without differentiating between case for at most n + characters. If the position where the two bstrings first differ is + before the nth position, the return value is the difference of the values + of the characters, otherwise 0 is returned. If the lengths are different + and less than n characters, then a difference from 0 is given, but if the + first extra character is '\0', then it is taken to be the value + UCHAR_MAX+1. + + .......................................................................... + + extern int bdestroy (bstring b); + + Deallocate the bstring passed. Passing NULL in as a parameter will have + no effect. Note that both the header and the data portion of the bstring + will be freed. No other bstring function which modifies one of its + parameters will free or reallocate the header. Because of this, in + general, bdestroy cannot be called on any declared struct tagbstring even + if it is not write protected. A bstring which is write protected cannot + be destroyed via the bdestroy call. Any attempt to do so will result in + no action taken, and BSTR_ERR will be returned. + + Note to C++ users: Passing in a CBString cast to a bstring will lead to + undefined behavior (free will be called on the header, rather than the + CBString destructor.) Instead just use the ordinary C++ language + facilities to dealloc a CBString. + + .......................................................................... + + extern int binstr (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + forward (increasing) direction. If it is found then it returns with the + first position after pos where it is found, otherwise it returns BSTR_ERR. + The algorithm used is brute force; O(m*n). + + .......................................................................... + + extern int binstrr (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + backward (decreasing) direction. If it is found then it returns with the + first position after pos where it is found, otherwise return BSTR_ERR. + Note that the current position at pos is tested as well -- so to be + disjoint from a previous forward search it is recommended that the + position be backed up (decremented) by one position. The algorithm used + is brute force; O(m*n). + + .......................................................................... + + extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + forward (increasing) direction but without regard to case. If it is + found then it returns with the first position after pos where it is + found, otherwise it returns BSTR_ERR. The algorithm used is brute force; + O(m*n). + + .......................................................................... + + extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2); + + Search for the bstring s2 in s1 starting at position pos and looking in a + backward (decreasing) direction but without regard to case. If it is + found then it returns with the first position after pos where it is + found, otherwise return BSTR_ERR. Note that the current position at pos + is tested as well -- so to be disjoint from a previous forward search it + is recommended that the position be backed up (decremented) by one + position. The algorithm used is brute force; O(m*n). + + .......................................................................... + + extern int binchr (const_bstring b0, int pos, const_bstring b1); + + Search for the first position in b0 starting from pos or after, in which + one of the characters in b1 is found. This function has an execution + time of O(b0->slen + b1->slen). If such a position does not exist in b0, + then BSTR_ERR is returned. + + .......................................................................... + + extern int binchrr (const_bstring b0, int pos, const_bstring b1); + + Search for the last position in b0 no greater than pos, in which one of + the characters in b1 is found. This function has an execution time + of O(b0->slen + b1->slen). If such a position does not exist in b0, + then BSTR_ERR is returned. + + .......................................................................... + + extern int bninchr (const_bstring b0, int pos, const_bstring b1); + + Search for the first position in b0 starting from pos or after, in which + none of the characters in b1 is found and return it. This function has + an execution time of O(b0->slen + b1->slen). If such a position does + not exist in b0, then BSTR_ERR is returned. + + .......................................................................... + + extern int bninchrr (const_bstring b0, int pos, const_bstring b1); + + Search for the last position in b0 no greater than pos, in which none of + the characters in b1 is found and return it. This function has an + execution time of O(b0->slen + b1->slen). If such a position does not + exist in b0, then BSTR_ERR is returned. + + .......................................................................... + + extern int bstrchr (const_bstring b, int c); + + Search for the character c in the bstring b forwards from the start of + the bstring. Returns the position of the found character or BSTR_ERR if + it is not found. + + NOTE: This has been implemented as a macro on top of bstrchrp (). + + .......................................................................... + + extern int bstrrchr (const_bstring b, int c); + + Search for the character c in the bstring b backwards from the end of the + bstring. Returns the position of the found character or BSTR_ERR if it is + not found. + + NOTE: This has been implemented as a macro on top of bstrrchrp (). + + .......................................................................... + + extern int bstrchrp (const_bstring b, int c, int pos); + + Search for the character c in b forwards from the position pos + (inclusive). Returns the position of the found character or BSTR_ERR if + it is not found. + + .......................................................................... + + extern int bstrrchrp (const_bstring b, int c, int pos); + + Search for the character c in b backwards from the position pos in bstring + (inclusive). Returns the position of the found character or BSTR_ERR if + it is not found. + + .......................................................................... + + extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill); + + Overwrite the bstring b0 starting at position pos with the bstring b1. If + the position pos is past the end of b0, then the character "fill" is + appended as necessary to make up the gap between the end of b0 and pos. + If b1 is NULL, it behaves as if it were a 0-length bstring. The value + BSTR_OK is returned if the operation is successful, otherwise BSTR_ERR is + returned. + + .......................................................................... + + extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill); + + Inserts the bstring s2 into s1 at position pos. If the position pos is + past the end of s1, then the character "fill" is appended as necessary to + make up the gap between the end of s1 and pos. The value BSTR_OK is + returned if the operation is successful, otherwise BSTR_ERR is returned. + + .......................................................................... + + extern int binsertch (bstring s1, int pos, int len, unsigned char fill); + + Inserts the character fill repeatedly into s1 at position pos for a + length len. If the position pos is past the end of s1, then the + character "fill" is appended as necessary to make up the gap between the + end of s1 and the position pos + len (exclusive). The value BSTR_OK is + returned if the operation is successful, otherwise BSTR_ERR is returned. + + .......................................................................... + + extern int breplace (bstring b1, int pos, int len, const_bstring b2, + unsigned char fill); + + Replace a section of a bstring from pos for a length len with the bstring + b2. If the position pos is past the end of b1 then the character "fill" + is appended as necessary to make up the gap between the end of b1 and + pos. + + .......................................................................... + + extern int bfindreplace (bstring b, const_bstring find, + const_bstring replace, int position); + + Replace all occurrences of the find substring with a replace bstring + after a given position in the bstring b. The find bstring must have a + length > 0 otherwise BSTR_ERR is returned. This function does not + perform recursive per character replacement; that is to say successive + searches resume at the position after the last replace. + + So for example: + + bfindreplace (a0 = bfromcstr("aabaAb"), a1 = bfromcstr("a"), + a2 = bfromcstr("aa"), 0); + + Should result in changing a0 to "aaaabaaAb". + + This function performs exactly (b->slen - position) bstring comparisons, + and data movement is bounded above by character volume equivalent to size + of the output bstring. + + .......................................................................... + + extern int bfindreplacecaseless (bstring b, const_bstring find, + const_bstring replace, int position); + + Replace all occurrences of the find substring, ignoring case, with a + replace bstring after a given position in the bstring b. The find bstring + must have a length > 0 otherwise BSTR_ERR is returned. This function + does not perform recursive per character replacement; that is to say + successive searches resume at the position after the last replace. + + So for example: + + bfindreplacecaseless (a0 = bfromcstr("AAbaAb"), a1 = bfromcstr("a"), + a2 = bfromcstr("aa"), 0); + + Should result in changing a0 to "aaaabaaaab". + + This function performs exactly (b->slen - position) bstring comparisons, + and data movement is bounded above by character volume equivalent to size + of the output bstring. + + .......................................................................... + + extern int balloc (bstring b, int length); + + Increase the allocated memory backing the data buffer for the bstring b + to a length of at least length. If the memory backing the bstring b is + already large enough, not action is performed. This has no effect on the + bstring b that is visible to the bstring API. Usually this function will + only be used when a minimum buffer size is required coupled with a direct + access to the ->data member of the bstring structure. + + Be warned that like any other bstring function, the bstring must be well + defined upon entry to this function. I.e., doing something like: + + b->slen *= 2; /* ?? Most likely incorrect */ + balloc (b, b->slen); + + is invalid, and should be implemented as: + + int t; + if (BSTR_OK == balloc (b, t = (b->slen * 2))) b->slen = t; + + This function will return with BSTR_ERR if b is not detected as a valid + bstring or length is not greater than 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int ballocmin (bstring b, int length); + + Change the amount of memory backing the bstring b to at least length. + This operation will never truncate the bstring data including the + extra terminating '\0' and thus will not decrease the length to less than + b->slen + 1. Note that repeated use of this function may cause + performance problems (realloc may be called on the bstring more than + the O(log(INT_MAX)) times). This function will return with BSTR_ERR if b + is not detected as a valid bstring or length is not greater than 0, + otherwise BSTR_OK is returned. + + So for example: + + if (BSTR_OK == ballocmin (b, 64)) b->data[63] = 'x'; + + The idea is that this will set the 64th character of b to 'x' if it is at + least 64 characters long otherwise do nothing. And we know this is well + defined so long as the ballocmin call was successfully, since it will + ensure that b has been allocated with at least 64 characters. + + .......................................................................... + + int btrunc (bstring b, int n); + + Truncate the bstring to at most n characters. This function will return + with BSTR_ERR if b is not detected as a valid bstring or n is less than + 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int bpattern (bstring b, int len); + + Replicate the starting bstring, b, end to end repeatedly until it + surpasses len characters, then chop the result to exactly len characters. + This function operates in-place. This function will return with BSTR_ERR + if b is NULL or of length 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int btoupper (bstring b); + + Convert contents of bstring to upper case. This function will return with + BSTR_ERR if b is NULL or of length 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int btolower (bstring b); + + Convert contents of bstring to lower case. This function will return with + BSTR_ERR if b is NULL or of length 0, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int bltrimws (bstring b); + + Delete whitespace contiguous from the left end of the bstring. This + function will return with BSTR_ERR if b is NULL or of length 0, otherwise + BSTR_OK is returned. + + .......................................................................... + + extern int brtrimws (bstring b); + + Delete whitespace contiguous from the right end of the bstring. This + function will return with BSTR_ERR if b is NULL or of length 0, otherwise + BSTR_OK is returned. + + .......................................................................... + + extern int btrimws (bstring b); + + Delete whitespace contiguous from both ends of the bstring. This function + will return with BSTR_ERR if b is NULL or of length 0, otherwise BSTR_OK + is returned. + + .......................................................................... + + extern int bstrListCreate (void); + + Create an empty struct bstrList. The struct bstrList output structure is + declared as follows: + + struct bstrList { + int qty, mlen; + bstring * entry; + }; + + The entry field actually is an array with qty number entries. The mlen + record counts the maximum number of bstring's for which there is memory + in the entry record. + + The Bstrlib API does *NOT* include a comprehensive set of functions for + full management of struct bstrList in an abstracted way. The reason for + this is because aliasing semantics of the list are best left to the user + of this function, and performance varies wildly depending on the + assumptions made. For a complete list of bstring data type it is + recommended that the C++ public std::vector be used, since its + semantics are usage are more standard. + + .......................................................................... + + extern int bstrListDestroy (struct bstrList * sl); + + Destroy a struct bstrList structure that was returned by the bsplit + function. Note that this will destroy each bstring in the ->entry array + as well. See bstrListCreate() above for structure of struct bstrList. + + .......................................................................... + + extern int bstrListAlloc (struct bstrList * sl, int msz); + + Ensure that there is memory for at least msz number of entries for the + list. + + .......................................................................... + + extern int bstrListAllocMin (struct bstrList * sl, int msz); + + Try to allocate the minimum amount of memory for the list to include at + least msz entries or sl->qty whichever is greater. + + .......................................................................... + + extern struct bstrList * bsplit (bstring str, unsigned char splitChar); + + Create an array of sequential substrings from str divided by the + character splitChar. Successive occurrences of the splitChar will be + divided by empty bstring entries, following the semantics from the Python + programming language. To reclaim the memory from this output structure, + bstrListDestroy () should be called. See bstrListCreate() above for + structure of struct bstrList. + + .......................................................................... + + extern struct bstrList * bsplits (bstring str, const_bstring splitStr); + + Create an array of sequential substrings from str divided by any + character contained in splitStr. An empty splitStr causes a single entry + bstrList containing a copy of str to be returned. See bstrListCreate() + above for structure of struct bstrList. + + .......................................................................... + + extern struct bstrList * bsplitstr (bstring str, const_bstring splitStr); + + Create an array of sequential substrings from str divided by the entire + substring splitStr. An empty splitStr causes a single entry bstrList + containing a copy of str to be returned. See bstrListCreate() above for + structure of struct bstrList. + + .......................................................................... + + extern bstring bjoin (const struct bstrList * bl, const_bstring sep); + + Join the entries of a bstrList into one bstring by sequentially + concatenating them with the sep bstring in between. If sep is NULL, it + is treated as if it were the empty bstring. Note that: + + bjoin (l = bsplit (b, s->data[0]), s); + + should result in a copy of b, if s->slen is 1. If there is an error NULL + is returned, otherwise a bstring with the correct result is returned. + See bstrListCreate() above for structure of struct bstrList. + + .......................................................................... + + extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + + Iterate the set of disjoint sequential substrings over str starting at + position pos divided by the character splitChar. The parm passed to + bsplitcb is passed on to cb. If the function cb returns a value < 0, + then further iterating is halted and this value is returned by bsplitcb. + + Note: Non-destructive modification of str from within the cb function + while performing this split is not undefined. bsplitcb behaves in + sequential lock step with calls to cb. I.e., after returning from a cb + that return a non-negative integer, bsplitcb continues from the position + 1 character after the last detected split character and it will halt + immediately if the length of str falls below this point. However, if the + cb function destroys str, then it *must* return with a negative value, + otherwise bsplitcb will continue in an undefined manner. + + This function is provided as an incremental alternative to bsplit that is + abortable and which does not impose additional memory allocation. + + .......................................................................... + + extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + + Iterate the set of disjoint sequential substrings over str starting at + position pos divided by any of the characters in splitStr. An empty + splitStr causes the whole str to be iterated once. The parm passed to + bsplitcb is passed on to cb. If the function cb returns a value < 0, + then further iterating is halted and this value is returned by bsplitcb. + + Note: Non-destructive modification of str from within the cb function + while performing this split is not undefined. bsplitscb behaves in + sequential lock step with calls to cb. I.e., after returning from a cb + that return a non-negative integer, bsplitscb continues from the position + 1 character after the last detected split character and it will halt + immediately if the length of str falls below this point. However, if the + cb function destroys str, then it *must* return with a negative value, + otherwise bsplitscb will continue in an undefined manner. + + This function is provided as an incremental alternative to bsplits that + is abortable and which does not impose additional memory allocation. + + .......................................................................... + + extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, + int (* cb) (void * parm, int ofs, int len), void * parm); + + Iterate the set of disjoint sequential substrings over str starting at + position pos divided by the entire substring splitStr. An empty splitStr + causes each character of str to be iterated. The parm passed to bsplitcb + is passed on to cb. If the function cb returns a value < 0, then further + iterating is halted and this value is returned by bsplitcb. + + Note: Non-destructive modification of str from within the cb function + while performing this split is not undefined. bsplitstrcb behaves in + sequential lock step with calls to cb. I.e., after returning from a cb + that return a non-negative integer, bsplitstrcb continues from the position + 1 character after the last detected split character and it will halt + immediately if the length of str falls below this point. However, if the + cb function destroys str, then it *must* return with a negative value, + otherwise bsplitscb will continue in an undefined manner. + + This function is provided as an incremental alternative to bsplitstr that + is abortable and which does not impose additional memory allocation. + + .......................................................................... + + extern bstring bformat (const char * fmt, ...); + + Takes the same parameters as printf (), but rather than outputting + results to stdio, it forms a bstring which contains what would have been + output. Note that if there is an early generation of a '\0' character, + the bstring will be truncated to this end point. + + Note that %s format tokens correspond to '\0' terminated char * buffers, + not bstrings. To print a bstring, first dereference data element of the + the bstring: + + /* b1->data needs to be '\0' terminated, so tagbstrings generated + by blk2tbstr () might not be suitable. */ + b0 = bformat ("Hello, %s", b1->data); + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bformat function is not present. + + .......................................................................... + + extern int bformata (bstring b, const char * fmt, ...); + + In addition to the initial output buffer b, bformata takes the same + parameters as printf (), but rather than outputting results to stdio, it + appends the results to the initial bstring parameter. Note that if + there is an early generation of a '\0' character, the bstring will be + truncated to this end point. + + Note that %s format tokens correspond to '\0' terminated char * buffers, + not bstrings. To print a bstring, first dereference data element of the + the bstring: + + /* b1->data needs to be '\0' terminated, so tagbstrings generated + by blk2tbstr () might not be suitable. */ + bformata (b0 = bfromcstr ("Hello"), ", %s", b1->data); + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bformata function is not present. + + .......................................................................... + + extern int bassignformat (bstring b, const char * fmt, ...); + + After the first parameter, it takes the same parameters as printf (), but + rather than outputting results to stdio, it outputs the results to + the bstring parameter b. Note that if there is an early generation of a + '\0' character, the bstring will be truncated to this end point. + + Note that %s format tokens correspond to '\0' terminated char * buffers, + not bstrings. To print a bstring, first dereference data element of the + the bstring: + + /* b1->data needs to be '\0' terminated, so tagbstrings generated + by blk2tbstr () might not be suitable. */ + bassignformat (b0 = bfromcstr ("Hello"), ", %s", b1->data); + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bassignformat function is not present. + + .......................................................................... + + extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist); + + The bvcformata function formats data under control of the format control + string fmt and attempts to append the result to b. The fmt parameter is + the same as that of the printf function. The variable argument list is + replaced with arglist, which has been initialized by the va_start macro. + The size of the output is upper bounded by count. If the required output + exceeds count, the string b is not augmented with any contents and a value + below BSTR_ERR is returned. If a value below -count is returned then it + is recommended that the negative of this value be used as an update to the + count in a subsequent pass. On other errors, such as running out of + memory, parameter errors or numeric wrap around BSTR_ERR is returned. + BSTR_OK is returned when the output is successfully generated and + appended to b. + + Note: There is no sanity checking of arglist, and this function is + destructive of the contents of b from the b->slen point onward. If there + is an early generation of a '\0' character, the bstring will be truncated + to this end point. + + Although this function is part of the external API for Bstrlib, the + interface and semantics (length limitations, and unusual return codes) + are fairly atypical. The real purpose for this function is to provide an + engine for the bvformata macro. + + Note that if the BSTRLIB_NOVSNP macro has been set when bstrlib has been + compiled the bvcformata function is not present. + + .......................................................................... + + extern bstring bread (bNread readPtr, void * parm); + typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, + void *parm); + + Read an entire stream into a bstring, verbatum. The readPtr function + pointer is compatible with fread sematics, except that it need not obtain + the stream data from a file. The intention is that parm would contain + the stream data context/state required (similar to the role of the FILE* + I/O stream parameter of fread.) + + Abstracting the block read function allows for block devices other than + file streams to be read if desired. Note that there is an ANSI + compatibility issue if "fread" is used directly; see the ANSI issues + section below. + + .......................................................................... + + extern int breada (bstring b, bNread readPtr, void * parm); + + Read an entire stream and append it to a bstring, verbatum. Behaves + like bread, except that it appends it results to the bstring b. + BSTR_ERR is returned on error, otherwise 0 is returned. + + .......................................................................... + + extern bstring bgets (bNgetc getcPtr, void * parm, char terminator); + typedef int (* bNgetc) (void * parm); + + Read a bstring from a stream. As many bytes as is necessary are read + until the terminator is consumed or no more characters are available from + the stream. If read from the stream, the terminator character will be + appended to the end of the returned bstring. The getcPtr function must + have the same semantics as the fgetc C library function (i.e., returning + an integer whose value is negative when there are no more characters + available, otherwise the value of the next available unsigned character + from the stream.) The intention is that parm would contain the stream + data context/state required (similar to the role of the FILE* I/O stream + parameter of fgets.) If no characters are read, or there is some other + detectable error, NULL is returned. + + bgets will never call the getcPtr function more often than necessary to + construct its output (including a single call, if required, to determine + that the stream contains no more characters.) + + Abstracting the character stream function and terminator character allows + for different stream devices and string formats other than '\n' + terminated lines in a file if desired (consider \032 terminated email + messages, in a UNIX mailbox for example.) + + For files, this function can be used analogously as fgets as follows: + + fp = fopen ( ... ); + if (fp) b = bgets ((bNgetc) fgetc, fp, '\n'); + + (Note that only one terminator character can be used, and that '\0' is + not assumed to terminate the stream in addition to the terminator + character. This is consistent with the semantics of fgets.) + + .......................................................................... + + extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator); + + Read from a stream and concatenate to a bstring. Behaves like bgets, + except that it appends it results to the bstring b. The value 1 is + returned if no characters are read before a negative result is returned + from getcPtr. Otherwise BSTR_ERR is returned on error, and 0 is returned + in other normal cases. + + .......................................................................... + + extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator); + + Read from a stream and concatenate to a bstring. Behaves like bgets, + except that it assigns the results to the bstring b. The value 1 is + returned if no characters are read before a negative result is returned + from getcPtr. Otherwise BSTR_ERR is returned on error, and 0 is returned + in other normal cases. + + .......................................................................... + + extern struct bStream * bsopen (bNread readPtr, void * parm); + + Wrap a given open stream (described by a fread compatible function + pointer and stream handle) into an open bStream suitable for the bstring + library streaming functions. + + .......................................................................... + + extern void * bsclose (struct bStream * s); + + Close the bStream, and return the handle to the stream that was + originally used to open the given stream. If s is NULL or detectably + invalid, NULL will be returned. + + .......................................................................... + + extern int bsbufflength (struct bStream * s, int sz); + + Set the length of the buffer used by the bStream. If sz is the macro + BSTR_BS_BUFF_LENGTH_GET (which is 0), the length is not set. If s is + NULL or sz is negative, the function will return with BSTR_ERR, otherwise + this function returns with the previous length. + + .......................................................................... + + extern int bsreadln (bstring r, struct bStream * s, char terminator); + + Read a bstring terminated by the terminator character or the end of the + stream from the bStream (s) and return it into the parameter r. The + matched terminator, if found, appears at the end of the line read. If + the stream has been exhausted of all available data, before any can be + read, BSTR_ERR is returned. This function may read additional characters + into the stream buffer from the core stream that are not returned, but + will be retained for subsequent read operations. When reading from high + speed streams, this function can perform significantly faster than bgets. + + .......................................................................... + + extern int bsreadlna (bstring r, struct bStream * s, char terminator); + + Read a bstring terminated by the terminator character or the end of the + stream from the bStream (s) and concatenate it to the parameter r. The + matched terminator, if found, appears at the end of the line read. If + the stream has been exhausted of all available data, before any can be + read, BSTR_ERR is returned. This function may read additional characters + into the stream buffer from the core stream that are not returned, but + will be retained for subsequent read operations. When reading from high + speed streams, this function can perform significantly faster than bgets. + + .......................................................................... + + extern int bsreadlns (bstring r, struct bStream * s, bstring terminators); + + Read a bstring terminated by any character in the terminators bstring or + the end of the stream from the bStream (s) and return it into the + parameter r. This function may read additional characters from the core + stream that are not returned, but will be retained for subsequent read + operations. + + .......................................................................... + + extern int bsreadlnsa (bstring r, struct bStream * s, bstring terminators); + + Read a bstring terminated by any character in the terminators bstring or + the end of the stream from the bStream (s) and concatenate it to the + parameter r. If the stream has been exhausted of all available data, + before any can be read, BSTR_ERR is returned. This function may read + additional characters from the core stream that are not returned, but + will be retained for subsequent read operations. + + .......................................................................... + + extern int bsread (bstring r, struct bStream * s, int n); + + Read a bstring of length n (or, if it is fewer, as many bytes as is + remaining) from the bStream. This function will read the minimum + required number of additional characters from the core stream. When the + stream is at the end of the file BSTR_ERR is returned, otherwise BSTR_OK + is returned. + + .......................................................................... + + extern int bsreada (bstring r, struct bStream * s, int n); + + Read a bstring of length n (or, if it is fewer, as many bytes as is + remaining) from the bStream and concatenate it to the parameter r. This + function will read the minimum required number of additional characters + from the core stream. When the stream is at the end of the file BSTR_ERR + is returned, otherwise BSTR_OK is returned. + + .......................................................................... + + extern int bsunread (struct bStream * s, const_bstring b); + + Insert a bstring into the bStream at the current position. These + characters will be read prior to those that actually come from the core + stream. + + .......................................................................... + + extern int bspeek (bstring r, const struct bStream * s); + + Return the number of currently buffered characters from the bStream that + will be read prior to reads from the core stream, and append it to the + the parameter r. + + .......................................................................... + + extern int bssplitscb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); + + Iterate the set of disjoint sequential substrings over the stream s + divided by any character from the bstring splitStr. The parm passed to + bssplitscb is passed on to cb. If the function cb returns a value < 0, + then further iterating is halted and this return value is returned by + bssplitscb. + + Note: At the point of calling the cb function, the bStream pointer is + pointed exactly at the position right after having read the split + character. The cb function can act on the stream by causing the bStream + pointer to move, and bssplitscb will continue by starting the next split + at the position of the pointer after the return from cb. + + However, if the cb causes the bStream s to be destroyed then the cb must + return with a negative value, otherwise bssplitscb will continue in an + undefined manner. + + This function is provided as way to incrementally parse through a file + or other generic stream that in total size may otherwise exceed the + practical or desired memory available. As with the other split callback + based functions this is abortable and does not impose additional memory + allocation. + + .......................................................................... + + extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, + int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); + + Iterate the set of disjoint sequential substrings over the stream s + divided by the entire substring splitStr. The parm passed to + bssplitstrcb is passed on to cb. If the function cb returns a + value < 0, then further iterating is halted and this return value is + returned by bssplitstrcb. + + Note: At the point of calling the cb function, the bStream pointer is + pointed exactly at the position right after having read the split + character. The cb function can act on the stream by causing the bStream + pointer to move, and bssplitstrcb will continue by starting the next + split at the position of the pointer after the return from cb. + + However, if the cb causes the bStream s to be destroyed then the cb must + return with a negative value, otherwise bssplitscb will continue in an + undefined manner. + + This function is provided as way to incrementally parse through a file + or other generic stream that in total size may otherwise exceed the + practical or desired memory available. As with the other split callback + based functions this is abortable and does not impose additional memory + allocation. + + .......................................................................... + + extern int bseof (const struct bStream * s); + + Return the defacto "EOF" (end of file) state of a stream (1 if the + bStream is in an EOF state, 0 if not, and BSTR_ERR if stream is closed or + detectably erroneous.) When the readPtr callback returns a value <= 0 + the stream reaches its "EOF" state. Note that bunread with non-empty + content will essentially turn off this state, and the stream will not be + in its "EOF" state so long as its possible to read more data out of it. + + Also note that the semantics of bseof() are slightly different from + something like feof(). I.e., reaching the end of the stream does not + necessarily guarantee that bseof() will return with a value indicating + that this has happened. bseof() will only return indicating that it has + reached the "EOF" and an attempt has been made to read past the end of + the bStream. + +The macros +---------- + + The macros described below are shown in a prototype form indicating their + intended usage. Note that the parameters passed to these macros will be + referenced multiple times. As with all macros, programmer care is + required to guard against unintended side effects. + + int blengthe (const_bstring b, int err); + + Returns the length of the bstring. If the bstring is NULL err is + returned. + + .......................................................................... + + int blength (const_bstring b); + + Returns the length of the bstring. If the bstring is NULL, the length + returned is 0. + + .......................................................................... + + int bchare (const_bstring b, int p, int c); + + Returns the p'th character of the bstring b. If the position p refers to + a position that does not exist in the bstring or the bstring is NULL, + then c is returned. + + .......................................................................... + + char bchar (const_bstring b, int p); + + Returns the p'th character of the bstring b. If the position p refers to + a position that does not exist in the bstring or the bstring is NULL, + then '\0' is returned. + + .......................................................................... + + char * bdatae (bstring b, char * err); + + Returns the char * data portion of the bstring b. If b is NULL, err is + returned. + + .......................................................................... + + char * bdata (bstring b); + + Returns the char * data portion of the bstring b. If b is NULL, NULL is + returned. + + .......................................................................... + + char * bdataofse (bstring b, int ofs, char * err); + + Returns the char * data portion of the bstring b offset by ofs. If b is + NULL, err is returned. + + .......................................................................... + + char * bdataofs (bstring b, int ofs); + + Returns the char * data portion of the bstring b offset by ofs. If b is + NULL, NULL is returned. + + .......................................................................... + + struct tagbstring var = bsStatic ("..."); + + The bsStatic macro allows for static declarations of literal string + constants as struct tagbstring structures. The resulting tagbstring does + not need to be freed or destroyed. Note that this macro is only well + defined for string literal arguments. For more general string pointers, + use the btfromcstr macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + <- bsStaticBlkParms ("...") + + The bsStaticBlkParms macro emits a pair of comma seperated parameters + corresponding to the block parameters for the block functions in Bstrlib + (i.e., blk2bstr, bcatblk, blk2tbstr, bisstemeqblk, bisstemeqcaselessblk.) + Note that this macro is only well defined for string literal arguments. + + Examples: + + bstring b = blk2bstr (bsStaticBlkParms ("Fast init. ")); + bcatblk (b, bsStaticBlkParms ("No frills fast concatenation.")); + + These are faster than using bfromcstr() and bcatcstr() respectively + because the length of the inline string is known as a compile time + constant. Also note that seperate struct tagbstring declarations for + holding the output of a bsStatic() macro are not required. + + .......................................................................... + + void btfromcstr (struct tagbstring& t, const char * s); + + Fill in the tagbstring t with the '\0' terminated char buffer s. This + action is purely reference oriented; no memory management is done. The + data member is just assigned s, and slen is assigned the strlen of s. + The s parameter is accessed exactly once in this macro. + + The resulting struct tagbstring is initially write protected. Attempts + to write to this struct tagbstring in a write protected state from any + bstrlib function will lead to BSTR_ERR being returned. Invoke the + bwriteallow on this struct tagbstring to make it writeable (though this + requires that s be obtained from a function compatible with malloc.) + + .......................................................................... + + void btfromblk (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len. This + action is purely reference oriented; no memory management is done. The + data member of t is just assigned s, and slen is assigned len. Note that + the buffer is not appended with a '\0' character. The s and len + parameters are accessed exactly once each in this macro. + + The resulting struct tagbstring is initially write protected. Attempts + to write to this struct tagbstring in a write protected state from any + bstrlib function will lead to BSTR_ERR being returned. Invoke the + bwriteallow on this struct tagbstring to make it writeable (though this + requires that s be obtained from a function compatible with malloc.) + + .......................................................................... + + void btfromblkltrimws (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len after it + has been left trimmed. This action is purely reference oriented; no + memory management is done. The data member of t is just assigned to a + pointer inside the buffer s. Note that the buffer is not appended with a + '\0' character. The s and len parameters are accessed exactly once each + in this macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + void btfromblkrtrimws (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len after it + has been right trimmed. This action is purely reference oriented; no + memory management is done. The data member of t is just assigned to a + pointer inside the buffer s. Note that the buffer is not appended with a + '\0' character. The s and len parameters are accessed exactly once each + in this macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + void btfromblktrimws (struct tagbstring& t, void * s, int len); + + Fill in the tagbstring t with the data buffer s with length len after it + has been left and right trimmed. This action is purely reference + oriented; no memory management is done. The data member of t is just + assigned to a pointer inside the buffer s. Note that the buffer is not + appended with a '\0' character. The s and len parameters are accessed + exactly once each in this macro. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct + tagbstring has no effect. + + .......................................................................... + + void bmid2tbstr (struct tagbstring& t, bstring b, int pos, int len); + + Fill the tagbstring t with the substring from b, starting from position + pos with a length len. The segment is clamped by the boundaries of + the bstring b. This action is purely reference oriented; no memory + management is done. Note that the buffer is not appended with a '\0' + character. Note that the t parameter to this macro may be accessed + multiple times. Note that the contents of t will become undefined + if the contents of b change or are destroyed. + + The resulting struct tagbstring is permanently write protected. Attempts + to write to this struct tagbstring in a write protected state from any + bstrlib function will lead to BSTR_ERR being returned. Invoking the + bwriteallow macro on this struct tagbstring will have no effect. + + .......................................................................... + + void bvformata (int& ret, bstring b, const char * format, lastarg); + + Append the bstring b with printf like formatting with the format control + string, and the arguments taken from the ... list of arguments after + lastarg passed to the containing function. If the containing function + does not have ... parameters or lastarg is not the last named parameter + before the ... then the results are undefined. If successful, the + results are appended to b and BSTR_OK is assigned to ret. Otherwise + BSTR_ERR is assigned to ret. + + Example: + + void dbgerror (FILE * fp, const char * fmt, ...) { + int ret; + bstring b; + bvformata (ret, b = bfromcstr ("DBG: "), fmt, fmt); + if (BSTR_OK == ret) fputs ((char *) bdata (b), fp); + bdestroy (b); + } + + Note that if the BSTRLIB_NOVSNP macro was set when bstrlib had been + compiled the bvformata macro will not link properly. If the + BSTRLIB_NOVSNP macro has been set, the bvformata macro will not be + available. + + .......................................................................... + + void bwriteprotect (struct tagbstring& t); + + Disallow bstring from being written to via the bstrlib API. Attempts to + write to the resulting tagbstring from any bstrlib function will lead to + BSTR_ERR being returned. + + Note: bstrings which are write protected cannot be destroyed via bdestroy. + + Note to C++ users: Setting a CBString as write protected will not prevent + it from being destroyed by the destructor. + + .......................................................................... + + void bwriteallow (struct tagbstring& t); + + Allow bstring to be written to via the bstrlib API. Note that such an + action makes the bstring both writable and destroyable. If the bstring is + not legitimately writable (as is the case for struct tagbstrings + initialized with a bsStatic value), the results of this are undefined. + + Note that invoking the bwriteallow macro may increase the number of + reallocs by one more than necessary for every call to bwriteallow + interleaved with any bstring API which writes to this bstring. + + .......................................................................... + + int biswriteprotected (struct tagbstring& t); + + Returns 1 if the bstring is write protected, otherwise 0 is returned. + +=============================================================================== + +The bstest module +----------------- + +The bstest module is just a unit test for the bstrlib module. For correct +implementations of bstrlib, it should execute with 0 failures being reported. +This test should be utilized if modifications/customizations to bstrlib have +been performed. It tests each core bstrlib function with bstrings of every +mode (read-only, NULL, static and mutable) and ensures that the expected +semantics are observed (including results that should indicate an error). It +also tests for aliasing support. Passing bstest is a necessary but not a +sufficient condition for ensuring the correctness of the bstrlib module. + + +The test module +--------------- + +The test module is just a unit test for the bstrwrap module. For correct +implementations of bstrwrap, it should execute with 0 failures being +reported. This test should be utilized if modifications/customizations to +bstrwrap have been performed. It tests each core bstrwrap function with +CBStrings write protected or not and ensures that the expected semantics are +observed (including expected exceptions.) Note that exceptions cannot be +disabled to run this test. Passing test is a necessary but not a sufficient +condition for ensuring the correctness of the bstrwrap module. + +=============================================================================== + +Using Bstring and CBString as an alternative to the C library +------------------------------------------------------------- + +First let us give a table of C library functions and the alternative bstring +functions and CBString methods that should be used instead of them. + +C-library Bstring alternative CBString alternative +--------- ------------------- -------------------- +gets bgets ::gets +strcpy bassign = operator +strncpy bassignmidstr ::midstr +strcat bconcat += operator +strncat bconcat + btrunc += operator + ::trunc +strtok bsplit, bsplits ::split +sprintf b(assign)format ::format +snprintf b(assign)format + btrunc ::format + ::trunc +vsprintf bvformata bvformata + +vsnprintf bvformata + btrunc bvformata + btrunc +vfprintf bvformata + fputs use bvformata + fputs +strcmp biseq, bstrcmp comparison operators. +strncmp bstrncmp, memcmp bstrncmp, memcmp +strlen ->slen, blength ::length +strdup bstrcpy constructor +strset bpattern ::fill +strstr binstr ::find +strpbrk binchr ::findchr +stricmp bstricmp cast & use bstricmp +strlwr btolower cast & use btolower +strupr btoupper cast & use btoupper +strrev bReverse (aux module) cast & use bReverse +strchr bstrchr cast & use bstrchr +strspnp use strspn use strspn +ungetc bsunread bsunread + +The top 9 C functions listed here are troublesome in that they impose memory +management in the calling function. The Bstring and CBstring interfaces have +built-in memory management, so there is far less code with far less potential +for buffer overrun problems. strtok can only be reliably called as a "leaf" +calculation, since it (quite bizarrely) maintains hidden internal state. And +gets is well known to be broken no matter what. The Bstrlib alternatives do +not suffer from those sorts of problems. + +The substitute for strncat can be performed with higher performance by using +the blk2tbstr macro to create a presized second operand for bconcat. + +C-library Bstring alternative CBString alternative +--------- ------------------- -------------------- +strspn strspn acceptable strspn acceptable +strcspn strcspn acceptable strcspn acceptable +strnset strnset acceptable strnset acceptable +printf printf acceptable printf acceptable +puts puts acceptable puts acceptable +fprintf fprintf acceptable fprintf acceptable +fputs fputs acceptable fputs acceptable +memcmp memcmp acceptable memcmp acceptable + +Remember that Bstring (and CBstring) functions will automatically append the +'\0' character to the character data buffer. So by simply accessing the data +buffer directly, ordinary C string library functions can be called directly +on them. Note that bstrcmp is not the same as memcmp in exactly the same way +that strcmp is not the same as memcmp. + +C-library Bstring alternative CBString alternative +--------- ------------------- -------------------- +fread balloc + fread ::alloc + fread +fgets balloc + fgets ::alloc + fgets + +These are odd ones because of the exact sizing of the buffer required. The +Bstring and CBString alternatives requires that the buffers are forced to +hold at least the prescribed length, then just use fread or fgets directly. +However, typically the automatic memory management of Bstring and CBstring +will make the typical use of fgets and fread to read specifically sized +strings unnecessary. + +Implementation Choices +---------------------- + +Overhead: +......... + +The bstring library has more overhead versus straight char buffers for most +functions. This overhead is essentially just the memory management and +string header allocation. This overhead usually only shows up for small +string manipulations. The performance loss has to be considered in +light of the following: + +1) What would be the performance loss of trying to write this management + code in one's own application? +2) Since the bstring library source code is given, a sufficiently powerful + modern inlining globally optimizing compiler can remove function call + overhead. + +Since the data type is exposed, a developer can replace any unsatisfactory +function with their own inline implementation. And that is besides the main +point of what the better string library is mainly meant to provide. Any +overhead lost has to be compared against the value of the safe abstraction +for coupling memory management and string functionality. + +Performance of the C interface: +............................... + +The algorithms used have performance advantages versus the analogous C +library functions. For example: + +1. bfromcstr/blk2str/bstrcpy versus strcpy/strdup. By using memmove instead + of strcpy, the break condition of the copy loop is based on an independent + counter (that should be allocated in a register) rather than having to + check the results of the load. Modern out-of-order executing CPUs can + parallelize the final branch mis-predict penality with the loading of the + source string. Some CPUs will also tend to have better built-in hardware + support for counted memory moves than load-compare-store. (This is a + minor, but non-zero gain.) +2. biseq versus strcmp. If the strings are unequal in length, bsiseq will + return in O(1) time. If the strings are aliased, or have aliased data + buffers, biseq will return in O(1) time. strcmp will always be O(k), + where k is the length of the common prefix or the whole string if they are + identical. +3. ->slen versus strlen. ->slen is obviously always O(1), while strlen is + always O(n) where n is the length of the string. +4. bconcat versus strcat. Both rely on precomputing the length of the + destination string argument, which will favor the bstring library. On + iterated concatenations the performance difference can be enormous. +5. bsreadln versus fgets. The bsreadln function reads large blocks at a time + from the given stream, then parses out lines from the buffers directly. + Some C libraries will implement fgets as a loop over single fgetc calls. + Testing indicates that the bsreadln approach can be several times faster + for fast stream devices (such as a file that has been entirely cached.) +6. bsplits/bsplitscb versus strspn. Accelerators for the set of match + characters are generated only once. +7. binstr versus strstr. The binstr implementation unrolls the loops to + help reduce loop overhead. This will matter if the target string is + long and source string is not found very early in the target string. + With strstr, while it is possible to unroll the source contents, it is + not possible to do so with the destination contents in a way that is + effective because every destination character must be tested against + '\0' before proceeding to the next character. +8. bReverse versus strrev. The C function must find the end of the string + first before swaping character pairs. +9. bstrrchr versus no comparable C function. Its not hard to write some C + code to search for a character from the end going backwards. But there + is no way to do this without computing the length of the string with + strlen. + +Practical testing indicates that in general Bstrlib is never signifcantly +slower than the C library for common operations, while very often having a +performance advantage that ranges from significant to massive. Even for +functions like b(n)inchr versus str(c)spn() (where, in theory, there is no +advantage for the Bstrlib architecture) the performance of Bstrlib is vastly +superior to most tested C library implementations. + +Some of Bstrlib's extra functionality also lead to inevitable performance +advantages over typical C solutions. For example, using the blk2tbstr macro, +one can (in O(1) time) generate an internal substring by reference while not +disturbing the original string. If disturbing the original string is not an +option, typically, a comparable char * solution would have to make a copy of +the substring to provide similar functionality. Another example is reverse +character set scanning -- the str(c)spn functions only scan in a forward +direction which can complicate some parsing algorithms. + +Where high performance char * based algorithms are available, Bstrlib can +still leverage them by accessing the ->data field on bstrings. So +realistically Bstrlib can never be significantly slower than any standard +'\0' terminated char * based solutions. + +Performance of the C++ interface: +................................. + +The C++ interface has been designed with an emphasis on abstraction and safety +first. However, since it is substantially a wrapper for the C bstring +functions, for longer strings the performance comments described in the +"Performance of the C interface" section above still apply. Note that the +(CBString *) type can be directly cast to a (bstring) type, and passed as +parameters to the C functions (though a CBString must never be passed to +bdestroy.) + +Probably the most controversial choice is performing full bounds checking on +the [] operator. This decision was made because 1) the fast alternative of +not bounds checking is still available by first casting the CBString to a +(const char *) buffer or to a (struct tagbstring) then derefencing .data and +2) because the lack of bounds checking is seen as one of the main weaknesses +of C/C++ versus other languages. This check being done on every access leads +to individual character extraction being actually slower than other languages +in this one respect (other language's compilers will normally dedicate more +resources on hoisting or removing bounds checking as necessary) but otherwise +bring C++ up to the level of other languages in terms of functionality. + +It is common for other C++ libraries to leverage the abstractions provided by +C++ to use reference counting and "copy on write" policies. While these +techniques can speed up some scenarios, they impose a problem with respect to +thread safety. bstrings and CBStrings can be properly protected with +"per-object" mutexes, meaning that two bstrlib calls can be made and execute +simultaneously, so long as the bstrings and CBstrings are distinct. With a +reference count and alias before copy on write policy, global mutexes are +required that prevent multiple calls to the strings library to execute +simultaneously regardless of whether or not the strings represent the same +string. + +One interesting trade off in CBString is that the default constructor is not +trivial. I.e., it always prepares a ready to use memory buffer. The purpose +is to ensure that there is a uniform internal composition for any functioning +CBString that is compatible with bstrings. It also means that the other +methods in the class are not forced to perform "late initialization" checks. +In the end it means that construction of CBStrings are slower than other +comparable C++ string classes. Initial testing, however, indicates that +CBString outperforms std::string and MFC's CString, for example, in all other +operations. So to work around this weakness it is recommended that CBString +declarations be pushed outside of inner loops. + +Practical testing indicates that with the exception of the caveats given +above (constructors and safe index character manipulations) the C++ API for +Bstrlib generally outperforms popular standard C++ string classes. Amongst +the standard libraries and compilers, the quality of concatenation operations +varies wildly and very little care has gone into search functions. Bstrlib +dominates those performance benchmarks. + +Memory management: +.................. + +The bstring functions which write and modify bstrings will automatically +reallocate the backing memory for the char buffer whenever it is required to +grow. The algorithm for resizing chosen is to snap up to sizes that are a +power of two which are sufficient to hold the intended new size. Memory +reallocation is not performed when the required size of the buffer is +decreased. This behavior can be relied on, and is necessary to make the +behaviour of balloc deterministic. This trades off additional memory usage +for decreasing the frequency for required reallocations: + +1. For any bstring whose size never exceeds n, its buffer is not ever + reallocated more than log_2(n) times for its lifetime. +2. For any bstring whose size never exceeds n, its buffer is never more than + 2*(n+1) in length. (The extra characters beyond 2*n are to allow for the + implicit '\0' which is always added by the bstring modifying functions.) + +Decreasing the buffer size when the string decreases in size would violate 1) +above and in real world case lead to pathological heap thrashing. Similarly, +allocating more tightly than "least power of 2 greater than necessary" would +lead to a violation of 1) and have the same potential for heap thrashing. + +Property 2) needs emphasizing. Although the memory allocated is always a +power of 2, for a bstring that grows linearly in size, its buffer memory also +grows linearly, not exponentially. The reason is that the amount of extra +space increases with each reallocation, which decreases the frequency of +future reallocations. + +Obviously, given that bstring writing functions may reallocate the data +buffer backing the target bstring, one should not attempt to cache the data +buffer address and use it after such bstring functions have been called. +This includes making reference struct tagbstrings which alias to a writable +bstring. + +balloc or bfromcstralloc can be used to preallocate the minimum amount of +space used for a given bstring. This will reduce even further the number of +times the data portion is reallocated. If the length of the string is never +more than one less than the memory length then there will be no further +reallocations. + +Note that invoking the bwriteallow macro may increase the number of reallocs +by one more than necessary for every call to bwriteallow interleaved with any +bstring API which writes to this bstring. + +The library does not use any mechanism for automatic clean up for the C API. +Thus explicit clean up via calls to bdestroy() are required to avoid memory +leaks. + +Constant and static tagbstrings: +................................ + +A struct tagbstring can be write protected from any bstrlib function using +the bwriteprotect macro. A write protected struct tagbstring can then be +reset to being writable via the bwriteallow macro. There is, of course, no +protection from attempts to directly access the bstring members. Modifying a +bstring which is write protected by direct access has undefined behavior. + +static struct tagbstrings can be declared via the bsStatic macro. They are +considered permanently unwritable. Such struct tagbstrings's are declared +such that attempts to write to it are not well defined. Invoking either +bwriteallow or bwriteprotect on static struct tagbstrings has no effect. + +struct tagbstring's initialized via btfromcstr or blk2tbstr are protected by +default but can be made writeable via the bwriteallow macro. If bwriteallow +is called on such struct tagbstring's, it is the programmer's responsibility +to ensure that: + +1) the buffer supplied was allocated from the heap. +2) bdestroy is not called on this tagbstring (unless the header itself has + also been allocated from the heap.) +3) free is called on the buffer to reclaim its memory. + +bwriteallow and bwriteprotect can be invoked on ordinary bstrings (they have +to be dereferenced with the (*) operator to get the levels of indirection +correct) to give them write protection. + +Buffer declaration: +................... + +The memory buffer is actually declared "unsigned char *" instead of "char *". +The reason for this is to trigger compiler warnings whenever uncasted char +buffers are assigned to the data portion of a bstring. This will draw more +diligent programmers into taking a second look at the code where they +have carelessly left off the typically required cast. (Research from +AT&T/Lucent indicates that additional programmer eyeballs is one of the most +effective mechanisms at ferreting out bugs.) + +Function pointers: +.................. + +The bgets, bread and bStream functions use function pointers to obtain +strings from data streams. The function pointer declarations have been +specifically chosen to be compatible with the fgetc and fread functions. +While this may seem to be a convoluted way of implementing fgets and fread +style functionality, it has been specifically designed this way to ensure +that there is no dependency on a single narrowly defined set of device +interfaces, such as just stream I/O. In the embedded world, its quite +possible to have environments where such interfaces may not exist in the +standard C library form. Furthermore, the generalization that this opens up +allows for more sophisticated uses for these functions (performing an fgets +like function on a socket, for example.) By using function pointers, it also +allows such abstract stream interfaces to be created using the bstring library +itself while not creating a circular dependency. + +Use of int's for sizes: +....................... + +This is just a recognition that 16bit platforms with requirements for strings +that are larger than 64K and 32bit+ platforms with requirements for strings +that are larger than 4GB are pretty marginal. The main focus is for 32bit +platforms, and emerging 64bit platforms with reasonable < 4GB string +requirements. Using ints allows for negative values which has meaning +internally to bstrlib. + +Semantic consideration: +....................... + +Certain care needs to be taken when copying and aliasing bstrings. A bstring +is essentially a pointer type which points to a multipart abstract data +structure. Thus usage, and lifetime of bstrings have semantics that follow +these considerations. For example: + + bstring a, b; + struct tagbstring t; + + a = bfromcstr("Hello"); /* Create new bstring and copy "Hello" into it. */ + b = a; /* Alias b to the contents of a. */ + t = *a; /* Create a current instance pseudo-alias of a. */ + bconcat (a, b); /* Double a and b, t is now undefined. */ + bdestroy (a); /* Destroy the contents of both a and b. */ + +Variables of type bstring are really just references that point to real +bstring objects. The equal operator (=) creates aliases, and the asterisk +dereference operator (*) creates a kind of alias to the current instance (which +is generally not useful for any purpose.) Using bstrcpy() is the correct way +of creating duplicate instances. The ampersand operator (&) is useful for +creating aliases to struct tagbstrings (remembering that constructed struct +tagbstrings are not writable by default.) + +CBStrings use complete copy semantics for the equal operator (=), and thus do +not have these sorts of issues. + +Debugging: +.......... + +Bstrings have a simple, exposed definition and construction, and the library +itself is open source. So most debugging is going to be fairly straight- +forward. But the memory for bstrings come from the heap, which can often be +corrupted indirectly, and it might not be obvious what has happened even from +direct examination of the contents in a debugger or a core dump. There are +some tools such as Purify, Insure++ and Electric Fence which can help solve +such problems, however another common approach is to directly instrument the +calls to malloc, realloc, calloc, free, memcpy, memmove and/or other calls +by overriding them with macro definitions. + +Although the user could hack on the Bstrlib sources directly as necessary to +perform such an instrumentation, Bstrlib comes with a built-in mechanism for +doing this. By defining the macro BSTRLIB_MEMORY_DEBUG and providing an +include file named memdbg.h this will force the core Bstrlib modules to +attempt to include this file. In such a file, macros could be defined which +overrides Bstrlib's useage of the C standard library. + +Rather than calling malloc, realloc, free, memcpy or memmove directly, Bstrlib +emits the macros bstr__alloc, bstr__realloc, bstr__free, bstr__memcpy and +bstr__memmove in their place respectively. By default these macros are simply +assigned to be equivalent to their corresponding C standard library function +call. However, if they are given earlier macro definitions (via the back +door include file) they will not be given their default definition. In this +way Bstrlib's interface to the standard library can be changed but without +having to directly redefine or link standard library symbols (both of which +are not strictly ANSI C compliant.) + +An example definition might include: + + #define bstr__alloc(sz) X_malloc ((sz), __LINE__, __FILE__) + +which might help contextualize heap entries in a debugging environment. + +The NULL parameter and sanity checking of bstrings is part of the Bstrlib +API, and thus Bstrlib itself does not present any different modes which would +correspond to "Debug" or "Release" modes. Bstrlib always contains mechanisms +which one might think of as debugging features, but retains the performance +and small memory footprint one would normally associate with release mode +code. + +Integration Microsoft's Visual Studio debugger: +............................................... + +Microsoft's Visual Studio debugger has a capability of customizable mouse +float over data type descriptions. This is accomplished by editting the +AUTOEXP.DAT file to include the following: + + ; new for CBString + tagbstring =slen= mlen= + Bstrlib::CBStringList =count= + +In Visual C++ 6.0 this file is located in the directory: + + C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin + +and in Visual Studio .NET 2003 its located here: + + C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\Debugger + +This will improve the ability of debugging with Bstrlib under Visual Studio. + +Security +-------- + +Bstrlib does not come with explicit security features outside of its fairly +comprehensive error detection, coupled with its strict semantic support. +That is to say that certain common security problems, such as buffer overrun, +constant overwrite, arbitrary truncation etc, are far less likely to happen +inadvertently. Where it does help, Bstrlib maximizes its advantage by +providing developers a simple adoption path that lets them leave less secure +string mechanisms behind. The library will not leave developers wanting, so +they will be less likely to add new code using a less secure string library +to add functionality that might be missing from Bstrlib. + +That said there are a number of security ideas not addressed by Bstrlib: + +1. Race condition exploitation (i.e., verifying a string's contents, then +raising the privilege level and execute it as a shell command as two +non-atomic steps) is well beyond the scope of what Bstrlib can provide. It +should be noted that MFC's built-in string mutex actually does not solve this +problem either -- it just removes immediate data corruption as a possible +outcome of such exploit attempts (it can be argued that this is worse, since +it will leave no trace of the exploitation). In general race conditions have +to be dealt with by careful design and implementation; it cannot be assisted +by a string library. + +2. Any kind of access control or security attributes to prevent usage in +dangerous interfaces such as system(). Perl includes a "trust" attribute +which can be endowed upon strings that are intended to be passed to such +dangerous interfaces. However, Perl's solution reflects its own limitations +-- notably that it is not a strongly typed language. In the example code for +Bstrlib, there is a module called taint.cpp. It demonstrates how to write a +simple wrapper class for managing "untainted" or trusted strings using the +type system to prevent questionable mixing of ordinary untrusted strings with +untainted ones then passing them to dangerous interfaces. In this way the +security correctness of the code reduces to auditing the direct usages of +dangerous interfaces or promotions of tainted strings to untainted ones. + +3. Encryption of string contents is way beyond the scope of Bstrlib. +Maintaining encrypted string contents in the futile hopes of thwarting things +like using system-level debuggers to examine sensitive string data is likely +to be a wasted effort (imagine a debugger that runs at a higher level than a +virtual processor where the application runs). For more standard encryption +usages, since the bstring contents are simply binary blocks of data, this +should pose no problem for usage with other standard encryption libraries. + +Compatibility +------------- + +The Better String Library is known to compile and function correctly with the +following compilers: + + - Microsoft Visual C++ + - Watcom C/C++ + - Intel's C/C++ compiler (Windows) + - The GNU C/C++ compiler (cygwin and Linux on PPC64) + - Borland C + - Turbo C + +Setting of configuration options should be unnecessary for these compilers +(unless exceptions are being disabled or STLport has been added to WATCOM +C/C++). Bstrlib has been developed with an emphasis on portability. As such +porting it to other compilers should be straight forward. This package +includes a porting guide (called porting.txt) which explains what issues may +exist for porting Bstrlib to different compilers and environments. + +ANSI issues +----------- + +1. The function pointer types bNgetc and bNread have prototypes which are very +similar to, but not exactly the same as fgetc and fread respectively. +Basically the FILE * parameter is replaced by void *. The purpose of this +was to allow one to create other functions with fgetc and fread like +semantics without being tied to ANSI C's file streaming mechanism. I.e., one +could very easily adapt it to sockets, or simply reading a block of memory, +or procedurally generated strings (for fractal generation, for example.) + +The problem is that invoking the functions (bNgetc)fgetc and (bNread)fread is +not technically legal in ANSI C. The reason being that the compiler is only +able to coerce the function pointers themselves into the target type, however +are unable to perform any cast (implicit or otherwise) on the parameters +passed once invoked. I.e., if internally void * and FILE * need some kind of +mechanical coercion, the compiler will not properly perform this conversion +and thus lead to undefined behavior. + +Apparently a platform from Data General called "Eclipse" and another from +Tandem called "NonStop" have a different representation for pointers to bytes +and pointers to words, for example, where coercion via casting is necessary. +(Actual confirmation of the existence of such machines is hard to come by, so +it is prudent to be skeptical about this information.) However, this is not +an issue for any known contemporary platforms. One may conclude that such +platforms are effectively apocryphal even if they do exist. + +To correctly work around this problem to the satisfaction of the ANSI +limitations, one needs to create wrapper functions for fgets and/or +fread with the prototypes of bNgetc and/or bNread respectively which performs +no other action other than to explicitely cast the void * parameter to a +FILE *, and simply pass the remaining parameters straight to the function +pointer call. + +The wrappers themselves are trivial: + + size_t freadWrap (void * buff, size_t esz, size_t eqty, void * parm) { + return fread (buff, esz, eqty, (FILE *) parm); + } + + int fgetcWrap (void * parm) { + return fgetc ((FILE *) parm); + } + +These have not been supplied in bstrlib or bstraux to prevent unnecessary +linking with file I/O functions. + +2. vsnprintf is not available on all compilers. Because of this, the bformat +and bformata functions (and format and formata methods) are not guaranteed to +work properly. For those compilers that don't have vsnprintf, the +BSTRLIB_NOVSNP macro should be set before compiling bstrlib, and the format +functions/method will be disabled. + +The more recent ANSI C standards have specified the required inclusion of a +vsnprintf function. + +3. The bstrlib function names are not unique in the first 6 characters. This +is only an issue for older C compiler environments which do not store more +than 6 characters for function names. + +4. The bsafe module defines macros and function names which are part of the +C library. This simply overrides the definition as expected on all platforms +tested, however it is not sanctioned by the ANSI standard. This module is +clearly optional and should be omitted on platforms which disallow its +undefined semantics. + +In practice the real issue is that some compilers in some modes of operation +can/will inline these standard library functions on a module by module basis +as they appear in each. The linker will thus have no opportunity to override +the implementation of these functions for those cases. This can lead to +inconsistent behaviour of the bsafe module on different platforms and +compilers. + +=============================================================================== + +Comparison with Microsoft's CString class +----------------------------------------- + +Although developed independently, CBStrings have very similar functionality to +Microsoft's CString class. However, the bstring library has significant +advantages over CString: + +1. Bstrlib is a C-library as well as a C++ library (using the C++ wrapper). + + - Thus it is compatible with more programming environments and + available to a wider population of programmers. + +2. The internal structure of a bstring is considered exposed. + + - A single contiguous block of data can be cut into read-only pieces by + simply creating headers, without allocating additional memory to create + reference copies of each of these sub-strings. + - In this way, using bstrings in a totally abstracted way becomes a choice + rather than an imposition. Further this choice can be made differently + at different layers of applications that use it. + +3. Static declaration support precludes the need for constructor + invocation. + + - Allows for static declarations of constant strings that has no + additional constructor overhead. + +4. Bstrlib is not attached to another library. + + - Bstrlib is designed to be easily plugged into any other library + collection, without dependencies on other libraries or paradigms (such + as "MFC".) + +The bstring library also comes with a few additional functions that are not +available in the CString class: + + - bsetstr + - bsplit + - bread + - breplace (this is different from CString::Replace()) + - Writable indexed characters (for example a[i]='x') + +Interestingly, although Microsoft did implement mid$(), left$() and right$() +functional analogues (these are functions from GWBASIC) they seem to have +forgotten that mid$() could be also used to write into the middle of a string. +This functionality exists in Bstrlib with the bsetstr() and breplace() +functions. + +Among the disadvantages of Bstrlib is that there is no special support for +localization or wide characters. Such things are considered beyond the scope +of what bstrings are trying to deliver. CString essentially supports the +older UCS-2 version of Unicode via widechar_t as an application-wide compile +time switch. + +CString's also use built-in mechanisms for ensuring thread safety under all +situations. While this makes writing thread safe code that much easier, this +built-in safety feature has a price -- the inner loops of each CString method +runs in its own critical section (grabbing and releasing a light weight mutex +on every operation.) The usual way to decrease the impact of a critical +section performance penalty is to amortize more operations per critical +section. But since the implementation of CStrings is fixed as a one critical +section per-operation cost, there is no way to leverage this common +performance enhancing idea. + +The search facilities in Bstrlib are comparable to those in MFC's CString +class, though it is missing locale specific collation. But because Bstrlib +is interoperable with C's char buffers, it will allow programmers to write +their own string searching mechanism (such as Boyer-Moore), or be able to +choose from a variety of available existing string searching libraries (such +as those for regular expressions) without difficulty. + +Microsoft used a very non-ANSI conforming trick in its implementation to +allow printf() to use the "%s" specifier to output a CString correctly. This +can be convenient, but it is inherently not portable. CBString requires an +explicit cast, while bstring requires the data member to be dereferenced. +Microsoft's own documentation recommends casting, instead of relying on this +feature. + +Comparison with C++'s std::string +--------------------------------- + +This is the C++ language's standard STL based string class. + +1. There is no C implementation. +2. The [] operator is not bounds checked. +3. Missing a lot of useful functions like printf-like formatting. +4. Some sub-standard std::string implementations (SGI) are necessarily unsafe + to use with multithreading. +5. Limited by STL's std::iostream which in turn is limited by ifstream which + can only take input from files. (Compare to CBStream's API which can take + abstracted input.) +6. Extremely uneven performance across implementations. + +Comparison with ISO C TR 24731 proposal +--------------------------------------- + +Following the ISO C99 standard, Microsoft has proposed a group of C library +extensions which are supposedly "safer and more secure". This proposal is +expected to be adopted by the ISO C standard which follows C99. + +The proposal reveals itself to be very similar to Microsoft's "StrSafe" +library. The functions are basically the same as other standard C library +string functions except that destination parameters are paired with an +additional length parameter of type rsize_t. rsize_t is the same as size_t, +however, the range is checked to make sure its between 1 and RSIZE_MAX. Like +Bstrlib, the functions perform a "parameter check". Unlike Bstrlib, when a +parameter check fails, rather than simply outputing accumulatable error +statuses, they call a user settable global error function handler, and upon +return of control performs no (additional) detrimental action. The proposal +covers basic string functions as well as a few non-reenterable functions +(asctime, ctime, and strtok). + +1. Still based solely on char * buffers (and therefore strlen() and strcat() + is still O(n), and there are no faster streq() comparison functions.) +2. No growable string semantics. +3. Requires manual buffer length synchronization in the source code. +4. No attempt to enhance functionality of the C library. +5. Introduces a new error scenario (strings exceeding RSIZE_MAX length). + +The hope is that by exposing the buffer length requirements there will be +fewer buffer overrun errors. However, the error modes are really just +transformed, rather than removed. The real problem of buffer overflows is +that they all happen as a result of erroneous programming. So forcing +programmers to manually deal with buffer limits, will make them more aware of +the problem but doesn't remove the possibility of erroneous programming. So +a programmer that erroneously mixes up the rsize_t parameters is no better off +from a programmer that introduces potential buffer overflows through other +more typical lapses. So at best this may reduce the rate of erroneous +programming, rather than making any attempt at removing failure modes. + +The error handler can discriminate between types of failures, but does not +take into account any callsite context. So the problem is that the error is +going to be manifest in a piece of code, but there is no pointer to that +code. It would seem that passing in the call site __FILE__, __LINE__ as +parameters would be very useful, but the API clearly doesn't support such a +thing (it would increase code bloat even more than the extra length +parameter does, and would require macro tricks to implement). + +The Bstrlib C API takes the position that error handling needs to be done at +the callsite, and just tries to make it as painless as possible. Furthermore, +error modes are removed by supporting auto-growing strings and aliasing. For +capturing errors in more central code fragments, Bstrlib's C++ API uses +exception handling extensively, which is superior to the leaf-only error +handler approach. + +Comparison with Managed String Library CERT proposal +---------------------------------------------------- + +The main webpage for the managed string library: +http://www.cert.org/secure-coding/managedstring.html + +Robert Seacord at CERT has proposed a C string library that he calls the +"Managed String Library" for C. Like Bstrlib, it introduces a new type +which is called a managed string. The structure of a managed string +(string_m) is like a struct tagbstring but missing the length field. This +internal structure is considered opaque. The length is, like the C standard +library, always computed on the fly by searching for a terminating NUL on +every operation that requires it. So it suffers from every performance +problem that the C standard library suffers from. Interoperating with C +string APIs (like printf, fopen, or anything else that takes a string +parameter) requires copying to additionally allocating buffers that have to +be manually freed -- this makes this library probably slower and more +cumbersome than any other string library in existence. + +The library gives a fully populated error status as the return value of every +string function. The hope is to be able to diagnose all problems +specifically from the return code alone. Comparing this to Bstrlib, which +aways returns one consistent error message, might make it seem that Bstrlib +would be harder to debug; but this is not true. With Bstrlib, if an error +occurs there is always enough information from just knowing there was an error +and examining the parameters to deduce exactly what kind of error has +happened. The managed string library thus gives up nested function calls +while achieving little benefit, while Bstrlib does not. + +One interesting feature that "managed strings" has is the idea of data +sanitization via character set whitelisting. That is to say, a globally +definable filter that makes any attempt to put invalid characters into strings +lead to an error and not modify the string. The author gives the following +example: + + // create valid char set + if (retValue = strcreate_m(&str1, "abc") ) { + fprintf( + stderr, + "Error %d from strcreate_m.\n", + retValue + ); + } + if (retValue = setcharset(str1)) { + fprintf( + stderr, + "Error %d from setcharset().\n", + retValue + ); + } + if (retValue = strcreate_m(&str1, "aabbccabc")) { + fprintf( + stderr, + "Error %d from strcreate_m.\n", + retValue + ); + } + // create string with invalid char set + if (retValue = strcreate_m(&str1, "abbccdabc")) { + fprintf( + stderr, + "Error %d from strcreate_m.\n", + retValue + ); + } + +Which we can compare with a more Bstrlib way of doing things: + + bstring bCreateWithFilter (const char * cstr, const_bstring filter) { + bstring b = bfromcstr (cstr); + if (BSTR_ERR != bninchr (b, filter) && NULL != b) { + fprintf (stderr, "Filter violation.\n"); + bdestroy (b); + b = NULL; + } + return b; + } + + struct tagbstring charFilter = bsStatic ("abc"); + bstring str1 = bCreateWithFilter ("aabbccabc", &charFilter); + bstring str2 = bCreateWithFilter ("aabbccdabc", &charFilter); + +The first thing we should notice is that with the Bstrlib approach you can +have different filters for different strings if necessary. Furthermore, +selecting a charset filter in the Managed String Library is uni-contextual. +That is to say, there can only be one such filter active for the entire +program, which means its usage is not well defined for intermediate library +usage (a library that uses it will interfere with user code that uses it, and +vice versa.) It is also likely to be poorly defined in multi-threading +environments. + +There is also a question as to whether the data sanitization filter is checked +on every operation, or just on creation operations. Since the charset can be +set arbitrarily at run time, it might be set *after* some managed strings have +been created. This would seem to imply that all functions should run this +additional check every time if there is an attempt to enforce this. This +would make things tremendously slow. On the other hand, if it is assumed that +only creates and other operations that take char *'s as input need be checked +because the charset was only supposed to be called once at and before any +other managed string was created, then one can see that its easy to cover +Bstrlib with equivalent functionality via a few wrapper calls such as the +example given above. + +And finally we have to question the value of sanitation in the first place. +For example, for httpd servers, there is generally a requirement that the +URLs parsed have some form that avoids undesirable translation to local file +system filenames or resources. The problem is that the way URLs can be +encoded, it must be completely parsed and translated to know if it is using +certain invalid character combinations. That is to say, merely filtering +each character one at a time is not necessarily the right way to ensure that +a string has safe contents. + +In the article that describes this proposal, it is claimed that it fairly +closely approximates the existing C API semantics. On this point we should +compare this "closeness" with Bstrlib: + + Bstrlib Managed String Library + ------- ---------------------- + +Pointer arithmetic Segment arithmetic N/A + +Use in C Std lib ->data, or bdata{e} getstr_m(x,*) ... free(x) + +String literals bsStatic, bsStaticBlk strcreate_m() + +Transparency Complete None + +Its pretty clear that the semantic mapping from C strings to Bstrlib is fairly +straightforward, and that in general semantic capabilities are the same or +superior in Bstrlib. On the other hand the Managed String Library is either +missing semantics or changes things fairly significantly. + +Comparison with Annexia's c2lib library +--------------------------------------- + +This library is available at: +http://www.annexia.org/freeware/c2lib + +1. Still based solely on char * buffers (and therefore strlen() and strcat() + is still O(n), and there are no faster streq() comparison functions.) + Their suggestion that alternatives which wrap the string data type (such as + bstring does) imposes a difficulty in interoperating with the C langauge's + ordinary C string library is not founded. +2. Introduction of memory (and vector?) abstractions imposes a learning + curve, and some kind of memory usage policy that is outside of the strings + themselves (and therefore must be maintained by the developer.) +3. The API is massive, and filled with all sorts of trivial (pjoin) and + controvertial (pmatch -- regular expression are not sufficiently + standardized, and there is a very large difference in performance between + compiled and non-compiled, REs) functions. Bstrlib takes a decidely + minimal approach -- none of the functionality in c2lib is difficult or + challenging to implement on top of Bstrlib (except the regex stuff, which + is going to be difficult, and controvertial no matter what.) +4. Understanding why c2lib is the way it is pretty much requires a working + knowledge of Perl. bstrlib requires only knowledge of the C string library + while providing just a very select few worthwhile extras. +5. It is attached to a lot of cruft like a matrix math library (that doesn't + include any functions for getting the determinant, eigenvectors, + eigenvalues, the matrix inverse, test for singularity, test for + orthogonality, a grahm schmit orthogonlization, LU decomposition ... I + mean why bother?) + +Convincing a development house to use c2lib is likely quite difficult. It +introduces too much, while not being part of any kind of standards body. The +code must therefore be trusted, or maintained by those that use it. While +bstring offers nothing more on this front, since its so much smaller, covers +far less in terms of scope, and will typically improve string performance, +the barrier to usage should be much smaller. + +Comparison with stralloc/qmail +------------------------------ + +More information about this library can be found here: +http://www.canonical.org/~kragen/stralloc.html or here: +http://cr.yp.to/lib/stralloc.html + +1. Library is very very minimal. A little too minimal. +2. Untargetted source parameters are not declared const. +3. Slightly different expected emphasis (like _cats function which takes an + ordinary C string char buffer as a parameter.) Its clear that the + remainder of the C string library is still required to perform more + useful string operations. + +The struct declaration for their string header is essentially the same as that +for bstring. But its clear that this was a quickly written hack whose goals +are clearly a subset of what Bstrlib supplies. For anyone who is served by +stralloc, Bstrlib is complete substitute that just adds more functionality. + +stralloc actually uses the interesting policy that a NULL data pointer +indicates an empty string. In this way, non-static empty strings can be +declared without construction. This advantage is minimal, since static empty +bstrings can be declared inline without construction, and if the string needs +to be written to it should be constructed from an empty string (or its first +initializer) in any event. + +wxString class +-------------- + +This is the string class used in the wxWindows project. A description of +wxString can be found here: +http://www.wxwindows.org/manuals/2.4.2/wx368.htm#wxstring + +This C++ library is similar to CBString. However, it is littered with +trivial functions (IsAscii, UpperCase, RemoveLast etc.) + +1. There is no C implementation. +2. The memory management strategy is to allocate a bounded fixed amount of + additional space on each resize, meaning that it does not have the + log_2(n) property that Bstrlib has (it will thrash very easily, cause + massive fragmentation in common heap implementations, and can easily be a + common source of performance problems). +3. The library uses a "copy on write" strategy, meaning that it has to deal + with multithreading problems. + +Vstr +---- + +This is a highly orthogonal C string library with an emphasis on +networking/realtime programming. It can be found here: +http://www.and.org/vstr/ + +1. The convoluted internal structure does not contain a '\0' char * compatible + buffer, so interoperability with the C library a non-starter. +2. The API and implementation is very large (owing to its orthogonality) and + can lead to difficulty in understanding its exact functionality. +3. An obvious dependency on gnu tools (confusing make configure step) +4. Uses a reference counting system, meaning that it is not likely to be + thread safe. + +The implementation has an extreme emphasis on performance for nontrivial +actions (adds, inserts and deletes are all constant or roughly O(#operations) +time) following the "zero copy" principle. This trades off performance of +trivial functions (character access, char buffer access/coersion, alias +detection) which becomes significantly slower, as well as incremental +accumulative costs for its searching/parsing functions. Whether or not Vstr +wins any particular performance benchmark will depend a lot on the benchmark, +but it should handily win on some, while losing dreadfully on others. + +The learning curve for Vstr is very steep, and it doesn't come with any +obvious way to build for Windows or other platforms without gnu tools. At +least one mechanism (the iterator) introduces a new undefined scenario +(writing to a Vstr while iterating through it.) Vstr has a very large +footprint, and is very ambitious in its total functionality. Vstr has no C++ +API. + +Vstr usage requires context initialization via vstr_init() which must be run +in a thread-local context. Given the totally reference based architecture +this means that sharing Vstrings across threads is not well defined, or at +least not safe from race conditions. This API is clearly geared to the older +standard of fork() style multitasking in UNIX, and is not safely transportable +to modern shared memory multithreading available in Linux and Windows. There +is no portable external solution making the library thread safe (since it +requires a mutex around each Vstr context -- not each string.) + +In the documentation for this library, a big deal is made of its self hosted +s(n)printf-like function. This is an issue for older compilers that don't +include vsnprintf(), but also an issue because Vstr has a slow conversion to +'\0' terminated char * mechanism. That is to say, using "%s" to format data +that originates from Vstr would be slow without some sort of native function +to do so. Bstrlib sidesteps the issue by relying on what snprintf-like +functionality does exist and having a high performance conversion to a char * +compatible string so that "%s" can be used directly. + +Str Library +----------- + +This is a fairly extensive string library, that includes full unicode support +and targetted at the goal of out performing MFC and STL. The architecture, +similarly to MFC's CStrings, is a copy on write reference counting mechanism. + +http://www.utilitycode.com/str/default.aspx + +1. Commercial. +2. C++ only. + +This library, like Vstr, uses a ref counting system. There is only so deeply +I can analyze it, since I don't have a license for it. However, performance +improvements over MFC's and STL, doesn't seem like a sufficient reason to +move your source base to it. For example, in the future, Microsoft may +improve the performance CString. + +It should be pointed out that performance testing of Bstrlib has indicated +that its relative performance advantage versus MFC's CString and STL's +std::string is at least as high as that for the Str library. + +libmib astrings +--------------- + +A handful of functional extensions to the C library that add dynamic string +functionality. +http://www.mibsoftware.com/libmib/astring/ + +This package basically references strings through char ** pointers and assumes +they are pointing to the top of an allocated heap entry (or NULL, in which +case memory will be newly allocated from the heap.) So its still up to user +to mix and match the older C string functions with these functions whenever +pointer arithmetic is used (i.e., there is no leveraging of the type system +to assert semantic differences between references and base strings as Bstrlib +does since no new types are introduced.) Unlike Bstrlib, exact string length +meta data is not stored, thus requiring a strlen() call on *every* string +writing operation. The library is very small, covering only a handful of C's +functions. + +While this is better than nothing, it is clearly slower than even the +standard C library, less safe and less functional than Bstrlib. + +To explain the advantage of using libmib, their website shows an example of +how dangerous C code: + + char buf[256]; + char *pszExtraPath = ";/usr/local/bin"; + + strcpy(buf,getenv("PATH")); /* oops! could overrun! */ + strcat(buf,pszExtraPath); /* Could overrun as well! */ + + printf("Checking...%s\n",buf); /* Some printfs overrun too! */ + +is avoided using libmib: + + char *pasz = 0; /* Must initialize to 0 */ + char *paszOut = 0; + char *pszExtraPath = ";/usr/local/bin"; + + if (!astrcpy(&pasz,getenv("PATH"))) /* malloc error */ exit(-1); + if (!astrcat(&pasz,pszExtraPath)) /* malloc error */ exit(-1); + + /* Finally, a "limitless" printf! we can use */ + asprintf(&paszOut,"Checking...%s\n",pasz);fputs(paszOut,stdout); + + astrfree(&pasz); /* Can use free(pasz) also. */ + astrfree(&paszOut); + +However, compare this to Bstrlib: + + bstring b, out; + + bcatcstr (b = bfromcstr (getenv ("PATH")), ";/usr/local/bin"); + out = bformat ("Checking...%s\n", bdatae (b, "")); + /* if (out && b) */ fputs (bdatae (out, ""), stdout); + bdestroy (b); + bdestroy (out); + +Besides being shorter, we can see that error handling can be deferred right +to the very end. Also, unlike the above two versions, if getenv() returns +with NULL, the Bstrlib version will not exhibit undefined behavior. +Initialization starts with the relevant content rather than an extra +autoinitialization step. + +libclc +------ + +An attempt to add to the standard C library with a number of common useful +functions, including additional string functions. +http://libclc.sourceforge.net/ + +1. Uses standard char * buffer, and adopts C 99's usage of "restrict" to pass + the responsibility to guard against aliasing to the programmer. +2. Adds no safety or memory management whatsoever. +3. Most of the supplied string functions are completely trivial. + +The goals of libclc and Bstrlib are clearly quite different. + +fireString +---------- + +http://firestuff.org/ + +1. Uses standard char * buffer, and adopts C 99's usage of "restrict" to pass + the responsibility to guard against aliasing to the programmer. +2. Mixes char * and length wrapped buffers (estr) functions, doubling the API + size, with safety limited to only half of the functions. + +Firestring was originally just a wrapper of char * functionality with extra +length parameters. However, it has been augmented with the inclusion of the +estr type which has similar functionality to stralloc. But firestring does +not nearly cover the functional scope of Bstrlib. + +Safe C String Library +--------------------- + +A library written for the purpose of increasing safety and power to C's string +handling capabilities. +http://www.zork.org/safestr/safestr.html + +1. While the safestr_* functions are safe in of themselves, interoperating + with char * string has dangerous unsafe modes of operation. +2. The architecture of safestr's causes the base pointer to change. Thus, + its not practical/safe to store a safestr in multiple locations if any + single instance can be manipulated. +3. Dependent on an additional error handling library. +4. Uses reference counting, meaning that it is either not thread safe or + slow and not portable. + +I think the idea of reallocating (and hence potentially changing) the base +pointer is a serious design flaw that is fatal to this architecture. True +safety is obtained by having automatic handling of all common scenarios +without creating implicit constraints on the user. + +Because of its automatic temporary clean up system, it cannot use "const" +semantics on input arguments. Interesting anomolies such as: + + safestr_t s, t; + s = safestr_replace (t = SAFESTR_TEMP ("This is a test"), + SAFESTR_TEMP (" "), SAFESTR_TEMP (".")); + /* t is now undefined. */ + +are possible. If one defines a function which takes a safestr_t as a +parameter, then the function would not know whether or not the safestr_t is +defined after it passes it to a safestr library function. The author +recommended method for working around this problem is to examine the +attributes of the safestr_t within the function which is to modify any of +its parameters and play games with its reference count. I think, therefore, +that the whole SAFESTR_TEMP idea is also fatally broken. + +The library implements immutability, optional non-resizability, and a "trust" +flag. This trust flag is interesting, and suggests that applying any +arbitrary sequence of safestr_* function calls on any set of trusted strings +will result in a trusted string. It seems to me, however, that if one wanted +to implement a trusted string semantic, one might do so by actually creating +a different *type* and only implement the subset of string functions that are +deemed safe (i.e., user input would be excluded, for example.) This, in +essence, would allow the compiler to enforce trust propogation at compile +time rather than run time. Non-resizability is also interesting, however, +it seems marginal (i.e., to want a string that cannot be resized, yet can be +modified and yet where a fixed sized buffer is undesirable.) + +=============================================================================== + +Examples +-------- + + Dumping a line numbered file: + + FILE * fp; + int i, ret; + struct bstrList * lines; + struct tagbstring prefix = bsStatic ("-> "); + + if (NULL != (fp = fopen ("bstrlib.txt", "rb"))) { + bstring b = bread ((bNread) fread, fp); + fclose (fp); + if (NULL != (lines = bsplit (b, '\n'))) { + for (i=0; i < lines->qty; i++) { + binsert (lines->entry[i], 0, &prefix, '?'); + printf ("%04d: %s\n", i, bdatae (lines->entry[i], "NULL")); + } + bstrListDestroy (lines); + } + bdestroy (b); + } + +For numerous other examples, see bstraux.c, bstraux.h and the example archive. + +=============================================================================== + +License +------- + +The Better String Library is available under either the 3 clause BSD license +(see the accompanying license.txt) or the Gnu Public License version 2 (see +the accompanying gpl.txt) at the option of the user. + +=============================================================================== + +Acknowledgements +---------------- + +The following individuals have made significant contributions to the design +and testing of the Better String Library: + +Bjorn Augestad +Clint Olsen +Darryl Bleau +Fabian Cenedese +Graham Wideman +Ignacio Burgueno +International Business Machines Corporation +Ira Mica +John Kortink +Manuel Woelker +Marcel van Kervinck +Michael Hsieh +Richard A. Smith +Simon Ekstrom +Wayne Scott + +=============================================================================== diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/license.txt b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/license.txt new file mode 100644 index 0000000000..cf78a984cc --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/license.txt @@ -0,0 +1,29 @@ +Copyright (c) 2002-2008 Paul Hsieh +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 bstrlib 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 OWNER 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. + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/porting.txt b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/porting.txt new file mode 100644 index 0000000000..11d8d13130 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/porting.txt @@ -0,0 +1,172 @@ +Better String library Porting Guide +----------------------------------- + +by Paul Hsieh + +The bstring library is an attempt to provide improved string processing +functionality to the C and C++ language. At the heart of the bstring library +is the management of "bstring"s which are a significant improvement over '\0' +terminated char buffers. See the accompanying documenation file bstrlib.txt +for more information. + +=============================================================================== + +Identifying the Compiler +------------------------ + +Bstrlib has been tested on the following compilers: + + Microsoft Visual C++ + Watcom C/C++ (32 bit flat) + Intel's C/C++ compiler (on Windows) + The GNU C/C++ compiler (on Windows/Linux on x86 and PPC64) + Borland C++ + Turbo C + +There are slight differences in these compilers which requires slight +differences in the implementation of Bstrlib. These are accomodated in the +same sources using #ifdef/#if defined() on compiler specific macros. To +port Bstrlib to a new compiler not listed above, it is recommended that the +same strategy be followed. If you are unaware of the compiler specific +identifying preprocessor macro for your compiler you might find it here: + +http://predef.sourceforge.net/precomp.html + +Note that Intel C/C++ on Windows sets the Microsoft identifier: _MSC_VER. + +16-bit vs. 32-bit vs. 64-bit Systems +------------------------------------ + +Bstrlib has been architected to deal with strings of length between 0 and +INT_MAX (inclusive). Since the values of int are never higher than size_t +there will be no issue here. Note that on most 64-bit systems int is 32-bit. + +Dependency on The C-Library +--------------------------- + +Bstrlib uses the functions memcpy, memmove, malloc, realloc, free and +vsnprintf. Many free standing C compiler implementations that have a mode in +which the C library is not available will typically not include these +functions which will make porting Bstrlib to it onerous. Bstrlib is not +designed for such bare bones compiler environments. This usually includes +compilers that target ROM environments. + +Porting Issues +-------------- + +Bstrlib has been written completely in ANSI/ISO C and ISO C++, however, there +are still a few porting issues. These are described below. + +1. The vsnprintf () function. + +Unfortunately, the earlier ANSI/ISO C standards did not include this function. +If the compiler of interest does not support this function then the +BSTRLIB_NOVSNP should be defined via something like: + + #if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP) + # if defined (__TURBOC__) || defined (__COMPILERVENDORSPECIFICMACRO__) + # define BSTRLIB_NOVSNP + # endif + #endif + +which appears at the top of bstrlib.h. Note that the bformat(a) functions +will not be declared or implemented if the BSTRLIB_NOVSNP macro is set. If +the compiler has renamed vsnprintf() to some other named function, then +search for the definition of the exvsnprintf macro in bstrlib.c file and be +sure its defined appropriately: + + #if defined (__COMPILERVENDORSPECIFICMACRO__) + # define exvsnprintf(r,b,n,f,a) {r=__compiler_specific_vsnprintf(b,n,f,a);} + #else + # define exvsnprintf(r,b,n,f,a) {r=vsnprintf(b,n,f,a);} + #endif + +Take notice of the return value being captured in the variable r. It is +assumed that r exceeds n if and only if the underlying vsnprintf function has +determined what the true maximal output length would be for output if the +buffer were large enough to hold it. Non-modern implementations must output a +lesser number (the macro can and should be modified to ensure this). + +2. Weak C++ compiler. + +C++ is a much more complicated language to implement than C. This has lead +to varying quality of compiler implementations. The weaknesses isolated in +the initial ports are inclusion of the Standard Template Library, +std::iostream and exception handling. By default it is assumed that the C++ +compiler supports all of these things correctly. If your compiler does not +support one or more of these define the corresponding macro: + + BSTRLIB_CANNOT_USE_STL + BSTRLIB_CANNOT_USE_IOSTREAM + BSTRLIB_DOESNT_THROW_EXCEPTIONS + +The compiler specific detected macro should be defined at the top of +bstrwrap.h in the Configuration defines section. Note that these disabling +macros can be overrided with the associated enabling macro if a subsequent +version of the compiler gains support. (For example, its possible to rig +up STLport to provide STL support for WATCOM C/C++, so -DBSTRLIB_CAN_USE_STL +can be passed in as a compiler option.) + +3. The bsafe module, and reserved words. + +The bsafe module is in gross violation of the ANSI/ISO C standard in the +sense that it redefines what could be implemented as reserved words on a +given compiler. The typical problem is that a compiler may inline some of the +functions and thus not be properly overridden by the definitions in the bsafe +module. It is also possible that a compiler may prohibit the redefinitions in +the bsafe module. Compiler specific action will be required to deal with +these situations. + +Platform Specific Files +----------------------- + +The makefiles for the examples are basically setup of for particular +environments for each platform. In general these makefiles are not portable +and should be constructed as necessary from scratch for each platform. + +Testing a port +-------------- + +To test that a port compiles correctly do the following: + +1. Build a sample project that includes the bstrlib, bstraux, bstrwrap, and + bsafe modules. +2. Compile bstest against the bstrlib module. +3. Run bstest and ensure that 0 errors are reported. +4. Compile test against the bstrlib and bstrwrap modules. +5. Run test and ensure that 0 errors are reported. +6. Compile each of the examples (except for the "re" example, which may be + complicated and is not a real test of bstrlib and except for the mfcbench + example which is Windows specific.) +7. Run each of the examples. + +The builds must have 0 errors, and should have the absolute minimum number of +warnings (in most cases can be reduced to 0.) The result of execution should +be essentially identical on each platform. + +Performance +----------- + +Different CPU and compilers have different capabilities in terms of +performance. It is possible for Bstrlib to assume performance +characteristics that a platform doesn't have (since it was primarily +developed on just one platform). The goal of Bstrlib is to provide very good +performance on all platforms regardless of this but without resorting to +extreme measures (such as using assembly language, or non-portable intrinsics +or library extensions.) + +There are two performance benchmarks that can be found in the example/ +directory. They are: cbench.c and cppbench.cpp. These are variations and +expansions of a benchmark for another string library. They don't cover all +string functionality, but do include the most basic functions which will be +common in most string manipulation kernels. + +............................................................................... + +Feedback +-------- + +In all cases, you may email issues found to the primary author of Bstrlib at +the email address: websnarf@users.sourceforge.net + +=============================================================================== diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/security.txt b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/security.txt new file mode 100644 index 0000000000..9761409f56 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/cbstring/security.txt @@ -0,0 +1,221 @@ +Better String library Security Statement +---------------------------------------- + +by Paul Hsieh + +=============================================================================== + +Introduction +------------ + +The Better String library (hereafter referred to as Bstrlib) is an attempt to +provide improved string processing functionality to the C and C++ languages. +At the heart of the Bstrlib is the management of "bstring"s which are a +significant improvement over '\0' terminated char buffers. See the +accompanying documenation file bstrlib.txt for more information. + +DISCLAIMER: 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 OWNER 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. + +Like any software, there is always a possibility of failure due to a flawed +implementation. Nevertheless a good faith effort has been made to minimize +such flaws in Bstrlib. Also, use of Bstrlib by itself will not make an +application secure or free from implementation failures. However, it is the +author's conviction that use of Bstrlib can greatly facilitate the creation +of software meeting the highest possible standards of security. + +Part of the reason why this document has been created, is for the purpose of +security auditing, or the creation of further "Statements on Security" for +software that is created that uses Bstrlib. An auditor may check the claims +below against Bstrlib, and use this as a basis for analysis of software which +uses Bstrlib. + +=============================================================================== + +Statement on Security +--------------------- + +This is a document intended to give consumers of the Better String Library +who are interested in security an idea of where the Better String Library +stands on various security issues. Any deviation observed in the actual +library itself from the descriptions below should be considered an +implementation error, not a design flaw. + +This statement is not an analytical proof of correctness or an outline of one +but rather an assertion similar to a scientific claim or hypothesis. By use, +testing and open independent examination (otherwise known as scientific +falsifiability), the credibility of the claims made below can rise to the +level of an established theory. + +Common security issues: +....................... + +1. Buffer Overflows + +The Bstrlib API allows the programmer a way to deal with strings without +having to deal with the buffers containing them. Ordinary usage of the +Bstrlib API itself makes buffer overflows impossible. + +Furthermore, the Bstrlib API has a superset of basic string functionality as +compared to the C library's char * functions, C++'s std::string class and +Microsoft's MFC based CString class. It also has abstracted mechanisms for +dealing with IO. This is important as it gives developers a way of migrating +all their code from a functionality point of view. + +2. Memory size overflow/wrap around attack + +Bstrlib is, by design, impervious to memory size overflow attacks. The +reason is it is resiliant to length overflows is that bstring lengths are +bounded above by INT_MAX, instead of ~(size_t)0. So length addition +overflows cause a wrap around of the integer value making them negative +causing balloc() to fail before an erroneous operation can occurr. Attempted +conversions of char * strings which may have lengths greater than INT_MAX are +detected and the conversion is aborted. + +It is unknown if this property holds on machines that don't represent +integers as 2s complement. It is recommended that Bstrlib be carefully +auditted by anyone using a system which is not 2s complement based. + +3. Constant string protection + +Bstrlib implements runtime enforced constant and read-only string semantics. +I.e., bstrings which are declared as constant via the bsStatic() macro cannot +be modified or deallocated directly through the Bstrlib API, and this cannot +be subverted by casting or other type coercion. This is independent of the +use of the const_bstring data type. + +The Bstrlib C API uses the type const_bstring to specify bstring parameters +whose contents do not change. Although the C language cannot enforce this, +this is nevertheless guaranteed by the implementation of the Bstrlib library +of C functions. The C++ API enforces the const attribute on CBString types +correctly. + +4. Aliased bstring support + +Bstrlib detects and supports aliased parameter management throughout the API. +The kind of aliasing that is allowed is the one where pointers of the same +basic type may be pointing to overlapping objects (this is the assumption the +ANSI C99 specification makes.) Each function behaves as if all read-only +parameters were copied to temporaries which are used in their stead before +the function is enacted (it rarely actually does this). No function in the +Bstrlib uses the "restrict" parameter attribute from the ANSI C99 +specification. + +5. Information leaking + +In bstraux.h, using the semantically equivalent macros bSecureDestroy() and +bSecureWriteProtect() in place of bdestroy() and bwriteprotect() respectively +will ensure that stale data does not linger in the heap's free space after +strings have been released back to memory. Created bstrings or CBStrings +are not linked to anything external to themselves, and thus cannot expose +deterministic data leaking. If a bstring is resized, the preimage may exist +as a copy that is released to the heap. Thus for sensitive data, the bstring +should be sufficiently presized before manipulated so that it is not resized. +bSecureInput() has been supplied in bstraux.c, which can be used to obtain +input securely without any risk of leaving any part of the input image in the +heap except for the allocated bstring that is returned. + +6. Memory leaking + +Bstrlib can be built using memdbg.h enabled via the BSTRLIB_MEMORY_DEBUG +macro. User generated definitions for malloc, realloc and free can then be +supplied which can implement special strategies for memory corruption +detection or memory leaking. Otherwise, bstrlib does not do anything out of +the ordinary to attempt to deal with the standard problem of memory leaking +(i.e., losing references to allocated memory) when programming in the C and +C++ languages. However, it does not compound the problem any more than exists +either, as it doesn't have any intrinsic inescapable leaks in it. Bstrlib +does not preclude the use of automatic garbage collection mechanisms such as +the Boehm garbage collector. + +7. Encryption + +Bstrlib does not present any built-in encryption mechanism. However, it +supports full binary contents in its data buffers, so any standard block +based encryption mechanism can make direct use of bstrings/CBStrings for +buffer management. + +8. Double freeing + +Freeing a pointer that is already free is an extremely rare, but nevertheless +a potentially ruthlessly corrupting operation (its possible to cause Win 98 to +reboot, by calling free mulitiple times on already freed data using the WATCOM +CRT.) Bstrlib invalidates the bstring header data before freeing, so that in +many cases a double free will be detected and an error will be reported +(though this behaviour is not guaranteed and should not be relied on). + +Using bstrFree pervasively (instead of bdestroy) can lead to somewhat +improved invalid free avoidance (it is completely safe whenever bstring +instances are only stored in unique variables). For example: + + struct tagbstring hw = bsStatic ("Hello, world"); + bstring cpHw = bstrcpy (&hw); + + #ifdef NOT_QUITE_AS_SAFE + bdestroy (cpHw); /* Never fail */ + bdestroy (cpHw); /* Error sometimes detected at runtime */ + bdestroy (&hw); /* Error detected at run time */ + #else + bstrFree (cpHw); /* Never fail */ + bstrFree (cpHw); /* Will do nothing */ + bstrFree (&hw); /* Will lead to a compile time error */ + #endif + +9. Resource based denial of service + +bSecureInput() has been supplied in bstraux.c. It has an optional upper limit +for input length. But unlike fgets(), it is also easily determined if the +buffer has been truncated early. In this way, a program can set an upper limit +on input sizes while still allowing for implementing context specific +truncation semantics (i.e., does the program consume but dump the extra +input, or does it consume it in later inputs?) + +10. Mixing char *'s and bstrings + +The bstring and char * representations are not identical. So there is a risk +when converting back and forth that data may lost. Essentially bstrings can +contain '\0' as a valid non-terminating character, while char * strings +cannot and in fact must use the character as a terminator. The risk of data +loss is very low, since: + + A) the simple method of only using bstrings in a char * semantically + compatible way is both easy to achieve and pervasively supported. + B) obtaining '\0' content in a string is either deliberate or indicative + of another, likely more serious problem in the code. + C) the library comes with various functions which deal with this issue + (namely: bfromcstr(), bstr2cstr (), and bSetCstrChar ()) + +Marginal security issues: +......................... + +11. 8-bit versus 9-bit portability + +Bstrlib uses CHAR_BIT and other limits.h constants to the maximum extent +possible to avoid portability problems. However, Bstrlib has not been tested +on any system that does not represent char as 8-bits. So whether or not it +works on 9-bit systems is an open question. It is recommended that Bstrlib be +carefully auditted by anyone using a system in which CHAR_BIT is not 8. + +12. EBCDIC/ASCII/UTF-8 data representation attacks. + +Bstrlib uses ctype.h functions to ensure that it remains portable to non- +ASCII systems. It also checks range to make sure it is well defined even for +data that ANSI does not define for the ctype functions. + +Obscure issues: +............... + +13. Data attributes + +There is no support for a Perl-like "taint" attribute, however, an example of +how to do this using C++'s type system is given as an example. + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/decode.c b/Code/Tools/HLSLCrossCompilerMETAL/src/decode.c new file mode 100644 index 0000000000..ce19d481d7 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/decode.c @@ -0,0 +1,1750 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/tokens.h" +#include "internal_includes/structs.h" +#include "internal_includes/decode.h" +#include "stdlib.h" +#include "stdio.h" +#include "internal_includes/reflect.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/toGLSLOperand.h" + +#define FOURCC(a, b, c, d) ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24)) +enum +{ + FOURCC_DXBC = FOURCC('D', 'X', 'B', 'C') +}; //DirectX byte code +enum +{ + FOURCC_SHDR = FOURCC('S', 'H', 'D', 'R') +}; //Shader model 4 code +enum +{ + FOURCC_SHEX = FOURCC('S', 'H', 'E', 'X') +}; //Shader model 5 code +enum +{ + FOURCC_RDEF = FOURCC('R', 'D', 'E', 'F') +}; //Resource definition (e.g. constant buffers) +enum +{ + FOURCC_ISGN = FOURCC('I', 'S', 'G', 'N') +}; //Input signature +enum +{ + FOURCC_IFCE = FOURCC('I', 'F', 'C', 'E') +}; //Interface (for dynamic linking) +enum +{ + FOURCC_OSGN = FOURCC('O', 'S', 'G', 'N') +}; //Output signature +enum +{ + FOURCC_PSGN = FOURCC('P', 'C', 'S', 'G') +}; //Patch-constant signature +enum +{ + FOURCC_FX10 = FOURCC('F', 'X', '1', '0') +}; //Effects 10 Binary data + +enum +{ + FOURCC_ISG1 = FOURCC('I', 'S', 'G', '1') +}; //Input signature with Stream and MinPrecision +enum +{ + FOURCC_OSG1 = FOURCC('O', 'S', 'G', '1') +}; //Output signature with Stream and MinPrecision +enum +{ + FOURCC_OSG5 = FOURCC('O', 'S', 'G', '5') +}; //Output signature with Stream + +typedef struct DXBCContainerHeaderTAG +{ + unsigned fourcc; + uint32_t unk[4]; + uint32_t one; + uint32_t totalSize; + uint32_t chunkCount; +} DXBCContainerHeader; + +typedef struct DXBCChunkHeaderTAG +{ + unsigned fourcc; + unsigned size; +} DXBCChunkHeader; + +#ifdef _DEBUG +static uint64_t operandID = 0; +static uint64_t instructionID = 0; +#endif + +#if defined(_WIN32) +#define osSprintf(dest, size, src) sprintf_s(dest, size, src) +#else +#define osSprintf(dest, size, src) sprintf(dest, src) +#endif + +void DecodeNameToken(const uint32_t* pui32NameToken, Operand* psOperand) +{ + const size_t MAX_BUFFER_SIZE = sizeof(psOperand->pszSpecialName); + psOperand->eSpecialName = DecodeOperandSpecialName(*pui32NameToken); + switch (psOperand->eSpecialName) + { + case NAME_UNDEFINED: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "undefined"); + break; + } + case NAME_POSITION: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "position"); + break; + } + case NAME_CLIP_DISTANCE: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "clipDistance"); + break; + } + case NAME_CULL_DISTANCE: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "cullDistance"); + break; + } + case NAME_RENDER_TARGET_ARRAY_INDEX: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "renderTargetArrayIndex"); + break; + } + case NAME_VIEWPORT_ARRAY_INDEX: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "viewportArrayIndex"); + break; + } + case NAME_VERTEX_ID: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "vertexID"); + break; + } + case NAME_PRIMITIVE_ID: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "primitiveID"); + break; + } + case NAME_INSTANCE_ID: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "instanceID"); + break; + } + case NAME_IS_FRONT_FACE: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "isFrontFace"); + break; + } + case NAME_SAMPLE_INDEX: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "sampleIndex"); + break; + } + //For the quadrilateral domain, there are 6 factors (4 sides, 2 inner). + case NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR: + case NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR: + case NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR: + + //For the triangular domain, there are 4 factors (3 sides, 1 inner) + case NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR: + case NAME_FINAL_TRI_INSIDE_TESSFACTOR: + + //For the isoline domain, there are 2 factors (detail and density). + case NAME_FINAL_LINE_DETAIL_TESSFACTOR: + case NAME_FINAL_LINE_DENSITY_TESSFACTOR: + { + osSprintf(psOperand->pszSpecialName, MAX_BUFFER_SIZE, "tessFactor"); + break; + } + default: + { + ASSERT(0); + break; + } + } + + return; +} + +// Find the declaration of the texture described by psTextureOperand and +// mark it as a shadow type. (e.g. accessed via sampler2DShadow rather than sampler2D) +void MarkTextureAsShadow(ShaderInfo* psShaderInfo, Declaration* psDeclList, const uint32_t ui32DeclCount, const Operand* psTextureOperand) +{ + (void)psShaderInfo; + + Declaration* psDecl = psDeclList; + uint32_t i; + + ASSERT(psTextureOperand->eType == OPERAND_TYPE_RESOURCE); + + for (i = 0; i < ui32DeclCount; ++i) + { + if (psDecl->eOpcode == OPCODE_DCL_RESOURCE) + { + if (psDecl->asOperands[0].eType == OPERAND_TYPE_RESOURCE && + psDecl->asOperands[0].ui32RegisterNumber == psTextureOperand->ui32RegisterNumber) + { + psDecl->ui32IsShadowTex = 1; + break; + } + } + psDecl++; + } +} + +// Search through the list. Return the index if the value is found, return 0xffffffff if not found +static uint32_t Find(uint32_t* psList, uint32_t ui32Count, uint32_t ui32Value) +{ + uint32_t i; + for (i = 0; i < ui32Count; i++) + { + if (psList[i] == ui32Value) + { + return i; + } + } + return 0xffffffff; +} + +void MarkTextureSamplerPair(ShaderInfo* psShaderInfo, Declaration* psDeclList, const uint32_t ui32DeclCount, const Operand* psTextureOperand, const Operand* psSamplerOperand, TextureSamplerInfo* psTextureSamplerInfo) +{ + Declaration* psDecl = psDeclList; + uint32_t i; + bstring combinedname; + const char* cstr; + + ASSERT(psTextureOperand->eType == OPERAND_TYPE_RESOURCE); + ASSERT(psSamplerOperand->eType == OPERAND_TYPE_SAMPLER); + + for (i = 0; i < ui32DeclCount; ++i) + { + if (psDecl->eOpcode == OPCODE_DCL_RESOURCE) + { + if (psDecl->asOperands[0].eType == OPERAND_TYPE_RESOURCE && + psDecl->asOperands[0].ui32RegisterNumber == psTextureOperand->ui32RegisterNumber) + { + // psDecl is the texture resource referenced by psTextureOperand + ASSERT(psDecl->ui32SamplerUsedCount < MAX_TEXTURE_SAMPLERS_PAIRS); + + // add psSamplerOperand->ui32RegisterNumber to list of samplers that use this texture + if (Find(psDecl->ui32SamplerUsed, psDecl->ui32SamplerUsedCount, psSamplerOperand->ui32RegisterNumber) == 0xffffffff) + { + psDecl->ui32SamplerUsed[psDecl->ui32SamplerUsedCount++] = psSamplerOperand->ui32RegisterNumber; + + // Record the texturename_X_samplername string in the TextureSamplerPair array that we return to the client + ASSERT(psTextureSamplerInfo->ui32NumTextureSamplerPairs < MAX_RESOURCE_BINDINGS); + combinedname = TextureSamplerName(psShaderInfo, psTextureOperand->ui32RegisterNumber, psSamplerOperand->ui32RegisterNumber, psDecl->ui32IsShadowTex); + cstr = bstr2cstr(combinedname, '\0'); + bdestroy(combinedname); + strcpy(psTextureSamplerInfo->aTextureSamplerPair[psTextureSamplerInfo->ui32NumTextureSamplerPairs++].Name, cstr); + } + break; + } + } + psDecl++; + } +} + +uint32_t DecodeOperand (const uint32_t* pui32Tokens, Operand* psOperand) +{ + int i; + uint32_t ui32NumTokens = 1; + OPERAND_NUM_COMPONENTS eNumComponents; + +#ifdef _DEBUG + psOperand->id = operandID++; +#endif + + //Some defaults + psOperand->iWriteMaskEnabled = 1; + psOperand->iGSInput = 0; + psOperand->aeDataType[0] = SVT_FLOAT; + psOperand->aeDataType[1] = SVT_FLOAT; + psOperand->aeDataType[2] = SVT_FLOAT; + psOperand->aeDataType[3] = SVT_FLOAT; + + psOperand->iExtended = DecodeIsOperandExtended(*pui32Tokens); + + + psOperand->eModifier = OPERAND_MODIFIER_NONE; + psOperand->psSubOperand[0] = 0; + psOperand->psSubOperand[1] = 0; + psOperand->psSubOperand[2] = 0; + + psOperand->eMinPrecision = OPERAND_MIN_PRECISION_DEFAULT; + + /* Check if this instruction is extended. If it is, + * we need to print the information first */ + if (psOperand->iExtended) + { + /* OperandToken1 is the second token */ + ui32NumTokens++; + + if (DecodeExtendedOperandType(pui32Tokens[1]) == EXTENDED_OPERAND_MODIFIER) + { + psOperand->eModifier = DecodeExtendedOperandModifier(pui32Tokens[1]); + psOperand->eMinPrecision = DecodeOperandMinPrecision(pui32Tokens[1]); + } + } + + psOperand->iIndexDims = DecodeOperandIndexDimension(*pui32Tokens); + psOperand->eType = DecodeOperandType(*pui32Tokens); + + psOperand->ui32RegisterNumber = 0; + + eNumComponents = DecodeOperandNumComponents(*pui32Tokens); + + if (psOperand->eType == OPERAND_TYPE_INPUT_GS_INSTANCE_ID) + { + eNumComponents = OPERAND_1_COMPONENT; + psOperand->aeDataType[0] = SVT_UINT; + } + + switch (eNumComponents) + { + case OPERAND_1_COMPONENT: + { + psOperand->iNumComponents = 1; + break; + } + case OPERAND_4_COMPONENT: + { + psOperand->iNumComponents = 4; + break; + } + default: + { + psOperand->iNumComponents = 0; + break; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + psOperand->eSelMode = DecodeOperand4CompSelMode(*pui32Tokens); + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + psOperand->ui32CompMask = DecodeOperand4CompMask(*pui32Tokens); + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + psOperand->ui32Swizzle = DecodeOperand4CompSwizzle(*pui32Tokens); + + if (psOperand->ui32Swizzle != NO_SWIZZLE) + { + psOperand->aui32Swizzle[0] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 0); + psOperand->aui32Swizzle[1] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 1); + psOperand->aui32Swizzle[2] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 2); + psOperand->aui32Swizzle[3] = DecodeOperand4CompSwizzleSource(*pui32Tokens, 3); + } + else + { + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_X; + psOperand->aui32Swizzle[1] = OPERAND_4_COMPONENT_Y; + psOperand->aui32Swizzle[2] = OPERAND_4_COMPONENT_Z; + psOperand->aui32Swizzle[3] = OPERAND_4_COMPONENT_W; + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psOperand->aui32Swizzle[0] = DecodeOperand4CompSel1(*pui32Tokens); + } + } + + //Set externally to this function based on the instruction opcode. + psOperand->iIntegerImmediate = 0; + + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32) + { + for (i = 0; i < psOperand->iNumComponents; ++i) + { + psOperand->afImmediates[i] = *((float*)(&pui32Tokens[ui32NumTokens])); + ui32NumTokens++; + } + } + else + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + for (i = 0; i < psOperand->iNumComponents; ++i) + { + psOperand->adImmediates[i] = *((double*)(&pui32Tokens[ui32NumTokens])); + ui32NumTokens += 2; + } + } + + if (psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH) + { + psOperand->ui32RegisterNumber = -1; + psOperand->ui32CompMask = -1; + } + + for (i = 0; i < psOperand->iIndexDims; ++i) + { + OPERAND_INDEX_REPRESENTATION eRep = DecodeOperandIndexRepresentation(i, *pui32Tokens); + + psOperand->eIndexRep[i] = eRep; + + psOperand->aui32ArraySizes[i] = 0; + psOperand->ui32RegisterNumber = 0; + + switch (eRep) + { + case OPERAND_INDEX_IMMEDIATE32: + { + psOperand->ui32RegisterNumber = *(pui32Tokens + ui32NumTokens); + psOperand->aui32ArraySizes[i] = psOperand->ui32RegisterNumber; + break; + } + case OPERAND_INDEX_RELATIVE: + { + psOperand->psSubOperand[i] = hlslcc_malloc(sizeof(Operand)); + DecodeOperand(pui32Tokens + ui32NumTokens, psOperand->psSubOperand[i]); + + ui32NumTokens++; + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + psOperand->ui32RegisterNumber = *(pui32Tokens + ui32NumTokens); + psOperand->aui32ArraySizes[i] = psOperand->ui32RegisterNumber; + + ui32NumTokens++; + + psOperand->psSubOperand[i] = hlslcc_malloc(sizeof(Operand)); + DecodeOperand(pui32Tokens + ui32NumTokens, psOperand->psSubOperand[i]); + + ui32NumTokens++; + break; + } + default: + { + ASSERT(0); + break; + } + } + + ui32NumTokens++; + } + + psOperand->pszSpecialName[0] = '\0'; + + return ui32NumTokens; +} + +const uint32_t* DecodeDeclaration(ShaderData* psShader, const uint32_t* pui32Token, Declaration* psDecl) +{ + uint32_t ui32TokenLength = DecodeInstructionLength(*pui32Token); + const uint32_t bExtended = DecodeIsOpcodeExtended(*pui32Token); + const OPCODE_TYPE eOpcode = DecodeOpcodeType(*pui32Token); + uint32_t ui32OperandOffset = 1; + + if (eOpcode < NUM_OPCODES && eOpcode >= 0) + { + psShader->aiOpcodeUsed[eOpcode] = 1; + } + + psDecl->eOpcode = eOpcode; + + psDecl->ui32IsShadowTex = 0; + + if (bExtended) + { + ui32OperandOffset = 2; + } + + switch (eOpcode) + { + case OPCODE_DCL_RESOURCE: // DCL* opcodes have + { + psDecl->value.eResourceDimension = DecodeResourceDimension(*pui32Token); + psDecl->ui32NumOperands = 1; + psDecl->ui32SamplerUsedCount = 0; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_CONSTANT_BUFFER: // custom operand formats. + { + psDecl->value.eCBAccessPattern = DecodeConstantBufferAccessPattern(*pui32Token); + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_SAMPLER: + { + ResourceBinding* psBinding = 0; + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_SAMPLER && + GetResourceFromBindingPoint(RGROUP_SAMPLER, psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psBinding)) + { + psDecl->bIsComparisonSampler = psBinding->ui32Flags & SHADER_INPUT_FLAG_COMPARISON_SAMPLER; + } + break; + } + case OPCODE_DCL_INDEX_RANGE: + { + psDecl->ui32NumOperands = 1; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + psDecl->value.ui32IndexRange = pui32Token[ui32OperandOffset]; + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_INPUT) + { + uint32_t i; + const uint32_t indexRange = psDecl->value.ui32IndexRange; + const uint32_t reg = psDecl->asOperands[0].ui32RegisterNumber; + + psShader->aIndexedInput[reg] = indexRange; + psShader->aIndexedInputParents[reg] = reg; + + //-1 means don't declare this input because it falls in + //the range of an already declared array. + for (i = reg + 1; i < reg + indexRange; ++i) + { + psShader->aIndexedInput[i] = -1; + psShader->aIndexedInputParents[i] = reg; + } + } + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_OUTPUT) + { + psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.ui32IndexRange; + } + break; + } + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + { + psDecl->value.eOutputPrimitiveTopology = DecodeGSOutputPrimitiveTopology(*pui32Token); + break; + } + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + { + psDecl->value.eInputPrimitive = DecodeGSInputPrimitive(*pui32Token); + break; + } + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + { + psDecl->value.ui32MaxOutputVertexCount = pui32Token[1]; + break; + } + case OPCODE_DCL_TESS_PARTITIONING: + { + psDecl->value.eTessPartitioning = DecodeTessPartitioning(*pui32Token); + break; + } + case OPCODE_DCL_TESS_DOMAIN: + { + psDecl->value.eTessDomain = DecodeTessDomain(*pui32Token); + break; + } + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + { + psDecl->value.eTessOutPrim = DecodeTessOutPrim(*pui32Token); + break; + } + case OPCODE_DCL_THREAD_GROUP: + { + psDecl->value.aui32WorkGroupSize[0] = pui32Token[1]; + psDecl->value.aui32WorkGroupSize[1] = pui32Token[2]; + psDecl->value.aui32WorkGroupSize[2] = pui32Token[3]; + break; + } + case OPCODE_DCL_INPUT: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_INPUT_SIV: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + if (psShader->eShaderType == PIXEL_SHADER) + { + psDecl->value.eInterpolation = DecodeInterpolationMode(*pui32Token); + } + break; + } + case OPCODE_DCL_INPUT_PS: + { + psDecl->ui32NumOperands = 1; + psDecl->value.eInterpolation = DecodeInterpolationMode(*pui32Token); + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_PS_SGV: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + DecodeNameToken(pui32Token + 3, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_INPUT_PS_SIV: + { + psDecl->ui32NumOperands = 1; + psDecl->value.eInterpolation = DecodeInterpolationMode(*pui32Token); + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + DecodeNameToken(pui32Token + 3, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_OUTPUT: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_OUTPUT_SGV: + { + break; + } + case OPCODE_DCL_OUTPUT_SIV: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + DecodeNameToken(pui32Token + 3, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_TEMPS: + { + psDecl->value.ui32NumTemps = *(pui32Token + ui32OperandOffset); + break; + } + case OPCODE_DCL_INDEXABLE_TEMP: + { + psDecl->sIdxTemp.ui32RegIndex = *(pui32Token + ui32OperandOffset); + psDecl->sIdxTemp.ui32RegCount = *(pui32Token + ui32OperandOffset + 1); + psDecl->sIdxTemp.ui32RegComponentSize = *(pui32Token + ui32OperandOffset + 2); + break; + } + case OPCODE_DCL_GLOBAL_FLAGS: + { + psDecl->value.ui32GlobalFlags = DecodeGlobalFlags(*pui32Token); + break; + } + case OPCODE_DCL_INTERFACE: + { + uint32_t func = 0, numClassesImplementingThisInterface, arrayLen, interfaceID; + interfaceID = pui32Token[ui32OperandOffset]; + ui32OperandOffset++; + psDecl->ui32TableLength = pui32Token[ui32OperandOffset]; + ui32OperandOffset++; + + numClassesImplementingThisInterface = DecodeInterfaceTableLength(*(pui32Token + ui32OperandOffset)); + arrayLen = DecodeInterfaceArrayLength(*(pui32Token + ui32OperandOffset)); + + ui32OperandOffset++; + + psDecl->value.interface.ui32InterfaceID = interfaceID; + psDecl->value.interface.ui32NumFuncTables = numClassesImplementingThisInterface; + psDecl->value.interface.ui32ArraySize = arrayLen; + + psShader->funcPointer[interfaceID].ui32NumBodiesPerTable = psDecl->ui32TableLength; + + for (; func < numClassesImplementingThisInterface; ++func) + { + uint32_t ui32FuncTable = *(pui32Token + ui32OperandOffset); + psShader->aui32FuncTableToFuncPointer[ui32FuncTable] = interfaceID; + + psShader->funcPointer[interfaceID].aui32FuncTables[func] = ui32FuncTable; + ui32OperandOffset++; + } + + break; + } + case OPCODE_DCL_FUNCTION_BODY: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_FUNCTION_TABLE: + { + uint32_t ui32Func; + const uint32_t ui32FuncTableID = pui32Token[ui32OperandOffset++]; + const uint32_t ui32NumFuncsInTable = pui32Token[ui32OperandOffset++]; + + for (ui32Func = 0; ui32Func < ui32NumFuncsInTable; ++ui32Func) + { + const uint32_t ui32FuncBodyID = pui32Token[ui32OperandOffset++]; + + psShader->aui32FuncBodyToFuncTable[ui32FuncBodyID] = ui32FuncTableID; + + psShader->funcTable[ui32FuncTableID].aui32FuncBodies[ui32Func] = ui32FuncBodyID; + } + + // OpcodeToken0 is followed by a DWORD that represents the function table + // identifier and another DWORD (TableLength) that gives the number of + // functions in the table. + // + // This is followed by TableLength DWORDs which are function body indices. + // + + break; + } + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + { + break; + } + case OPCODE_HS_DECLS: + { + break; + } + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + { + psDecl->value.ui32MaxOutputVertexCount = DecodeOutputControlPointCount(*pui32Token); + break; + } + case OPCODE_HS_JOIN_PHASE: + case OPCODE_HS_FORK_PHASE: + case OPCODE_HS_CONTROL_POINT_PHASE: + { + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + ASSERT(psShader->asPhase[HS_FORK_PHASE].ui32InstanceCount != 0); //Check for wrapping when we decrement. + psDecl->value.aui32HullPhaseInstanceInfo[0] = psShader->asPhase[HS_FORK_PHASE].ui32InstanceCount - 1; + psDecl->value.aui32HullPhaseInstanceInfo[1] = pui32Token[1]; + break; + } + case OPCODE_CUSTOMDATA: + { + ui32TokenLength = pui32Token[1]; + { + const uint32_t ui32NumVec4 = (ui32TokenLength - 2) / 4; + uint32_t uIdx = 0; + + ICBVec4 const* pVec4Array = (void*) (pui32Token + 2); + + //The buffer will contain at least one value, but not more than 4096 scalars/1024 vec4's. + ASSERT(ui32NumVec4 < MAX_IMMEDIATE_CONST_BUFFER_VEC4_SIZE); + + /* must be a multiple of 4 */ + ASSERT(((ui32TokenLength - 2) % 4) == 0); + + for (uIdx = 0; uIdx < ui32NumVec4; uIdx++) + { + psDecl->asImmediateConstBuffer[uIdx] = pVec4Array[uIdx]; + } + + psDecl->ui32NumOperands = ui32NumVec4; + } + break; + } + case OPCODE_DCL_HS_MAX_TESSFACTOR: + { + psDecl->value.fMaxTessFactor = *((float*)&pui32Token[1]); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + { + psDecl->ui32NumOperands = 2; + psDecl->value.eResourceDimension = DecodeResourceDimension(*pui32Token); + psDecl->sUAV.ui32GloballyCoherentAccess = DecodeAccessCoherencyFlags(*pui32Token); + psDecl->sUAV.bCounter = 0; + psDecl->sUAV.ui32BufferSize = 0; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + psDecl->sUAV.Type = DecodeResourceReturnType(0, pui32Token[ui32OperandOffset]); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + { + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = DecodeAccessCoherencyFlags(*pui32Token); + psDecl->sUAV.bCounter = 0; + psDecl->sUAV.ui32BufferSize = 0; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + //This should be a RTYPE_UAV_RWBYTEADDRESS buffer. It is memory backed by + //a shader storage buffer whose is unknown at compile time. + psDecl->sUAV.ui32BufferSize = 0; + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + { + ResourceBinding* psBinding = NULL; + ConstantBuffer* psBuffer = NULL; + + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = DecodeAccessCoherencyFlags(*pui32Token); + psDecl->sUAV.bCounter = 0; + psDecl->sUAV.ui32BufferSize = 0; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + GetResourceFromBindingPoint(RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psBinding); + + GetConstantBufferFromBindingPoint(RGROUP_UAV, psBinding->ui32BindPoint, &psShader->sInfo, &psBuffer); + psDecl->sUAV.ui32BufferSize = psBuffer->ui32TotalSizeInBytes; + switch (psBinding->eType) + { + case RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER: + case RTYPE_UAV_APPEND_STRUCTURED: + case RTYPE_UAV_CONSUME_STRUCTURED: + psDecl->sUAV.bCounter = 1; + break; + default: + break; + } + break; + } + case OPCODE_DCL_RESOURCE_STRUCTURED: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_RESOURCE_RAW: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + { + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = 0; + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + psDecl->sTGSM.ui32Stride = pui32Token[ui32OperandOffset++]; + psDecl->sTGSM.ui32Count = pui32Token[ui32OperandOffset++]; + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW: + { + psDecl->ui32NumOperands = 1; + psDecl->sUAV.ui32GloballyCoherentAccess = 0; + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + + psDecl->sTGSM.ui32Stride = 4; + psDecl->sTGSM.ui32Count = pui32Token[ui32OperandOffset++]; + break; + } + case OPCODE_DCL_STREAM: + { + psDecl->ui32NumOperands = 1; + DecodeOperand(pui32Token + ui32OperandOffset, &psDecl->asOperands[0]); + break; + } + case OPCODE_DCL_GS_INSTANCE_COUNT: + { + psDecl->ui32NumOperands = 0; + psDecl->value.ui32GSInstanceCount = pui32Token[1]; + break; + } + default: + { + //Reached end of declarations + return 0; + } + } + + return pui32Token + ui32TokenLength; +} + +const uint32_t* DeocdeInstruction(const uint32_t* pui32Token, Instruction* psInst, ShaderData* psShader) +{ + uint32_t ui32TokenLength = DecodeInstructionLength(*pui32Token); + const uint32_t bExtended = DecodeIsOpcodeExtended(*pui32Token); + const OPCODE_TYPE eOpcode = DecodeOpcodeType(*pui32Token); + uint32_t ui32OperandOffset = 1; + +#ifdef _DEBUG + psInst->id = instructionID++; +#endif + + psInst->eOpcode = eOpcode; + + psInst->bSaturate = DecodeInstructionSaturate(*pui32Token); + + psInst->bAddressOffset = 0; + + psInst->ui32FirstSrc = 1; + + if (bExtended) + { + do + { + const uint32_t ui32ExtOpcodeToken = pui32Token[ui32OperandOffset]; + const EXTENDED_OPCODE_TYPE eExtType = DecodeExtendedOpcodeType(ui32ExtOpcodeToken); + + if (eExtType == EXTENDED_OPCODE_SAMPLE_CONTROLS) + { + struct + { + int i4 : 4; + } sU; + struct + { + int i4 : 4; + } sV; + struct + { + int i4 : 4; + } sW; + + psInst->bAddressOffset = 1; + + sU.i4 = DecodeImmediateAddressOffset( + IMMEDIATE_ADDRESS_OFFSET_U, ui32ExtOpcodeToken); + sV.i4 = DecodeImmediateAddressOffset( + IMMEDIATE_ADDRESS_OFFSET_V, ui32ExtOpcodeToken); + sW.i4 = DecodeImmediateAddressOffset( + IMMEDIATE_ADDRESS_OFFSET_W, ui32ExtOpcodeToken); + + psInst->iUAddrOffset = sU.i4; + psInst->iVAddrOffset = sV.i4; + psInst->iWAddrOffset = sW.i4; + } + else if (eExtType == EXTENDED_OPCODE_RESOURCE_RETURN_TYPE) + { + psInst->xType = DecodeExtendedResourceReturnType(0, ui32ExtOpcodeToken); + psInst->yType = DecodeExtendedResourceReturnType(1, ui32ExtOpcodeToken); + psInst->zType = DecodeExtendedResourceReturnType(2, ui32ExtOpcodeToken); + psInst->wType = DecodeExtendedResourceReturnType(3, ui32ExtOpcodeToken); + } + else if (eExtType == EXTENDED_OPCODE_RESOURCE_DIM) + { + psInst->eResDim = DecodeExtendedResourceDimension(ui32ExtOpcodeToken); + } + + ui32OperandOffset++; + } + while (DecodeIsOpcodeExtended(pui32Token[ui32OperandOffset - 1])); + } + + if (eOpcode < NUM_OPCODES && eOpcode >= 0) + { + psShader->aiOpcodeUsed[eOpcode] = 1; + } + + switch (eOpcode) + { + //no operands + case OPCODE_CUT: + case OPCODE_EMIT: + case OPCODE_EMITTHENCUT: + case OPCODE_RET: + case OPCODE_LOOP: + case OPCODE_ENDLOOP: + case OPCODE_BREAK: + case OPCODE_ELSE: + case OPCODE_ENDIF: + case OPCODE_CONTINUE: + case OPCODE_DEFAULT: + case OPCODE_ENDSWITCH: + case OPCODE_NOP: + case OPCODE_HS_CONTROL_POINT_PHASE: + case OPCODE_HS_FORK_PHASE: + case OPCODE_HS_JOIN_PHASE: + { + psInst->ui32NumOperands = 0; + psInst->ui32FirstSrc = 0; + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + psInst->ui32NumOperands = 0; + psInst->ui32FirstSrc = 0; + break; + } + case OPCODE_SYNC: + { + psInst->ui32NumOperands = 0; + psInst->ui32FirstSrc = 0; + psInst->ui32SyncFlags = DecodeSyncFlags(*pui32Token); + break; + } + + //1 operand + case OPCODE_EMIT_STREAM: + case OPCODE_CUT_STREAM: + case OPCODE_EMITTHENCUT_STREAM: + case OPCODE_CASE: + case OPCODE_SWITCH: + case OPCODE_LABEL: + { + psInst->ui32NumOperands = 1; + psInst->ui32FirstSrc = 0; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + + if (eOpcode == OPCODE_CASE) + { + psInst->asOperands[0].iIntegerImmediate = 1; + } + break; + } + + case OPCODE_INTERFACE_CALL: + { + psInst->ui32NumOperands = 1; + psInst->ui32FirstSrc = 0; + psInst->ui32FuncIndexWithinInterface = pui32Token[ui32OperandOffset]; + ui32OperandOffset++; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + + break; + } + + /* Floating point instruction decodes */ + + //Instructions with two operands go here + case OPCODE_MOV: + { + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + + //Mov with an integer dest. If src is an immediate then it must be encoded as an integer. + if (psInst->asOperands[0].eMinPrecision == OPERAND_MIN_PRECISION_SINT_16 || + psInst->asOperands[0].eMinPrecision == OPERAND_MIN_PRECISION_UINT_16) + { + psInst->asOperands[1].iIntegerImmediate = 1; + } + break; + } + case OPCODE_LOG: + case OPCODE_RSQ: + case OPCODE_EXP: + case OPCODE_SQRT: + case OPCODE_ROUND_PI: + case OPCODE_ROUND_NI: + case OPCODE_ROUND_Z: + case OPCODE_ROUND_NE: + case OPCODE_FRC: + case OPCODE_FTOU: + case OPCODE_FTOI: + case OPCODE_UTOF: + case OPCODE_ITOF: + case OPCODE_INEG: + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + case OPCODE_DMOV: + case OPCODE_DTOF: + case OPCODE_FTOD: + case OPCODE_DRCP: + case OPCODE_COUNTBITS: + case OPCODE_FIRSTBIT_HI: + case OPCODE_FIRSTBIT_LO: + case OPCODE_FIRSTBIT_SHI: + case OPCODE_BFREV: + case OPCODE_F32TOF16: + case OPCODE_F16TOF32: + case OPCODE_RCP: + case OPCODE_DERIV_RTX: + case OPCODE_DERIV_RTY: + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_NOT: + { + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + break; + } + + //Instructions with three operands go here + case OPCODE_SINCOS: + { + psInst->ui32FirstSrc = 2; + //Intentional fall-through + } + case OPCODE_IMIN: + case OPCODE_MIN: + case OPCODE_UMIN: + case OPCODE_IMAX: + case OPCODE_MAX: + case OPCODE_UMAX: + case OPCODE_MUL: + case OPCODE_DIV: + case OPCODE_ADD: + case OPCODE_DP2: + case OPCODE_DP3: + case OPCODE_DP4: + case OPCODE_NE: + case OPCODE_OR: + case OPCODE_XOR: + case OPCODE_LT: + case OPCODE_IEQ: + case OPCODE_IADD: + case OPCODE_AND: + case OPCODE_GE: + case OPCODE_IGE: + case OPCODE_EQ: + case OPCODE_USHR: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_LD: + case OPCODE_ILT: + case OPCODE_INE: + case OPCODE_UGE: + case OPCODE_ULT: + case OPCODE_ATOMIC_AND: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_IMAX: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_UMIN: + case OPCODE_DADD: + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DDIV: + { + psInst->ui32NumOperands = 3; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + //Instructions with four operands go here + case OPCODE_MAD: + case OPCODE_MOVC: + case OPCODE_IMAD: + case OPCODE_UDIV: + case OPCODE_LOD: + case OPCODE_SAMPLE: + case OPCODE_GATHER4: + case OPCODE_LD_MS: + case OPCODE_UBFE: + case OPCODE_IBFE: + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_DMOVC: + case OPCODE_DFMA: + case OPCODE_IMUL: + { + psInst->ui32NumOperands = 4; + + if (eOpcode == OPCODE_IMUL) + { + psInst->ui32FirstSrc = 2; + } + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + break; + } + case OPCODE_GATHER4_PO: + case OPCODE_SAMPLE_L: + case OPCODE_BFI: + case OPCODE_SWAPC: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { + psInst->ui32NumOperands = 5; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[4]); + break; + } + case OPCODE_GATHER4_C: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_B: + { + psInst->ui32NumOperands = 5; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[4]); + + /* sample_b is not a shadow sampler, others need flagging */ + if (eOpcode != OPCODE_SAMPLE_B) + { + MarkTextureAsShadow(&psShader->sInfo, + psShader->asPhase[MAIN_PHASE].ppsDecl[0], + psShader->asPhase[MAIN_PHASE].pui32DeclCount[0], &psInst->asOperands[2]); + } + + break; + } + case OPCODE_GATHER4_PO_C: + case OPCODE_SAMPLE_D: + { + psInst->ui32NumOperands = 6; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[4]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[5]); + + /* sample_d is not a shadow sampler, others need flagging */ + if (eOpcode != OPCODE_SAMPLE_D) + { + MarkTextureAsShadow(&psShader->sInfo, + psShader->asPhase[MAIN_PHASE].ppsDecl[0], + psShader->asPhase[MAIN_PHASE].pui32DeclCount[0], &psInst->asOperands[2]); + } + break; + } + case OPCODE_IF: + case OPCODE_BREAKC: + case OPCODE_CONTINUEC: + case OPCODE_RETC: + case OPCODE_DISCARD: + { + psInst->eBooleanTestType = DecodeInstrTestBool(*pui32Token); + psInst->ui32NumOperands = 1; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + break; + } + case OPCODE_CALLC: + { + psInst->eBooleanTestType = DecodeInstrTestBool(*pui32Token); + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + break; + } + case OPCODE_CUSTOMDATA: + { + psInst->ui32NumOperands = 0; + ui32TokenLength = pui32Token[1]; + break; + } + case OPCODE_EVAL_CENTROID: + { + psInst->ui32NumOperands = 2; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + break; + } + case OPCODE_EVAL_SAMPLE_INDEX: + case OPCODE_EVAL_SNAPPED: + case OPCODE_STORE_UAV_TYPED: + case OPCODE_LD_UAV_TYPED: + case OPCODE_LD_RAW: + case OPCODE_STORE_RAW: + { + psInst->ui32NumOperands = 3; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + case OPCODE_STORE_STRUCTURED: + case OPCODE_LD_STRUCTURED: + { + psInst->ui32NumOperands = 4; + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[3]); + break; + } + case OPCODE_RESINFO: + { + psInst->ui32NumOperands = 3; + + psInst->eResInfoReturnType = DecodeResInfoReturnType(pui32Token[0]); + + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[0]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[1]); + ui32OperandOffset += DecodeOperand(pui32Token + ui32OperandOffset, &psInst->asOperands[2]); + break; + } + case OPCODE_MSAD: + default: + { + ASSERT(0); + break; + } + } + + // For opcodes that sample textures, mark which samplers are used by each texture + { + uint32_t ui32TextureRegisterNumber; + uint32_t ui32SamplerRegisterNumber; + uint32_t bTextureSampleInstruction = 0; + switch (eOpcode) + { + case OPCODE_GATHER4: + // dest, coords, tex, sampler + ui32TextureRegisterNumber = 2; + ui32SamplerRegisterNumber = 3; + bTextureSampleInstruction = 1; + break; + case OPCODE_GATHER4_PO: + //dest, coords, offset, tex, sampler + ui32TextureRegisterNumber = 3; + ui32SamplerRegisterNumber = 4; + bTextureSampleInstruction = 1; + break; + case OPCODE_GATHER4_C: + //dest, coords, tex, sampler srcReferenceValue + ui32TextureRegisterNumber = 2; + ui32SamplerRegisterNumber = 3; + bTextureSampleInstruction = 1; + break; + case OPCODE_GATHER4_PO_C: + //dest, coords, offset, tex, sampler, srcReferenceValue + ui32TextureRegisterNumber = 3; + ui32SamplerRegisterNumber = 4; + bTextureSampleInstruction = 1; + break; + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_B: + case OPCODE_SAMPLE_D: + // dest, coords, tex, sampler [, reference] + ui32TextureRegisterNumber = 2; + ui32SamplerRegisterNumber = 3; + bTextureSampleInstruction = 1; + break; + } + + if (bTextureSampleInstruction) + { + MarkTextureSamplerPair(&psShader->sInfo, + psShader->asPhase[MAIN_PHASE].ppsDecl[0], + psShader->asPhase[MAIN_PHASE].pui32DeclCount[0], + &psInst->asOperands[ui32TextureRegisterNumber], + &psInst->asOperands[ui32SamplerRegisterNumber], + &psShader->textureSamplerInfo); + } + } + + UpdateOperandReferences(psShader, psInst); + + return pui32Token + ui32TokenLength; +} + +void BindTextureToSampler(ShaderData* psShader, uint32_t ui32TextureRegister, uint32_t ui32SamplerRegister) +{ + ASSERT(ui32TextureRegister < MAX_RESOURCE_BINDINGS && + (psShader->sInfo.aui32SamplerMap[ui32TextureRegister] == MAX_RESOURCE_BINDINGS || + psShader->sInfo.aui32SamplerMap[ui32TextureRegister] == ui32SamplerRegister)); + ASSERT(ui32SamplerRegister < MAX_RESOURCE_BINDINGS); + psShader->sInfo.aui32SamplerMap[ui32TextureRegister] = ui32SamplerRegister; +} + +void UpdateOperandReferences(ShaderData* psShader, Instruction* psInst) +{ + uint32_t ui32Operand; + const uint32_t ui32NumOperands = psInst->ui32NumOperands; + for (ui32Operand = 0; ui32Operand < ui32NumOperands; ++ui32Operand) + { + Operand* psOperand = &psInst->asOperands[ui32Operand]; + if (psOperand->eType == OPERAND_TYPE_INPUT || + psOperand->eType == OPERAND_TYPE_INPUT_CONTROL_POINT) + { + if (psOperand->iIndexDims == INDEX_2D) + { + if (psOperand->aui32ArraySizes[1] != 0)//gl_in[].gl_Position + { + psShader->abInputReferencedByInstruction[psOperand->ui32RegisterNumber] = 1; + } + } + else + { + psShader->abInputReferencedByInstruction[psOperand->ui32RegisterNumber] = 1; + } + } + } + + switch (psInst->eOpcode) + { + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_B: + case OPCODE_SAMPLE_D: + case OPCODE_GATHER4: + case OPCODE_GATHER4_C: + BindTextureToSampler(psShader, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber); + break; + case OPCODE_GATHER4_PO: + case OPCODE_GATHER4_PO_C: + BindTextureToSampler(psShader, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber); + break; + } +} + +const uint32_t* DecodeShaderPhase(const uint32_t* pui32Tokens, + ShaderData* psShader, + const uint32_t ui32Phase) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + const uint32_t ui32InstanceIndex = psShader->asPhase[ui32Phase].ui32InstanceCount; + + Instruction* psInst; + + //Declarations + Declaration* psDecl; + + //Using ui32ShaderLength as the declaration and instruction count + //will allocate more than enough memory. Avoids having to + //traverse the entire shader just to get the real counts. + + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + psShader->asPhase[ui32Phase].ppsDecl[ui32InstanceIndex] = psDecl; + psShader->asPhase[ui32Phase].pui32DeclCount[ui32InstanceIndex] = 0; + + psShader->asPhase[ui32Phase].ui32InstanceCount++; + + while (1) //Keep going until we reach the first non-declaration token, or the end of the shader. + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + psShader->asPhase[ui32Phase].pui32DeclCount[ui32InstanceIndex]++; + psDecl++; + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + + //Instructions + psInst = hlslcc_malloc(sizeof(Instruction) * ui32ShaderLength); + psShader->asPhase[ui32Phase].ppsInst[ui32InstanceIndex] = psInst; + psShader->asPhase[ui32Phase].pui32InstCount[ui32InstanceIndex] = 0; + + while (pui32CurrentToken < (psShader->pui32FirstToken + ui32ShaderLength)) + { + const uint32_t* nextInstr = DeocdeInstruction(pui32CurrentToken, psInst, psShader); + +#ifdef _DEBUG + if (nextInstr == pui32CurrentToken) + { + ASSERT(0); + break; + } +#endif + + if (psInst->eOpcode == OPCODE_HS_FORK_PHASE) + { + return pui32CurrentToken; + } + else if (psInst->eOpcode == OPCODE_HS_JOIN_PHASE) + { + return pui32CurrentToken; + } + pui32CurrentToken = nextInstr; + psShader->asPhase[ui32Phase].pui32InstCount[ui32InstanceIndex]++; + + psInst++; + } + + return pui32CurrentToken; +} + +void AllocateHullPhaseArrays(const uint32_t* pui32Tokens, + ShaderData* psShader, + uint32_t ui32Phase, + OPCODE_TYPE ePhaseOpcode) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + uint32_t ui32InstanceCount = 0; + + while (1) //Keep going until we reach the first non-declaration token, or the end of the shader. + { + uint32_t ui32TokenLength = DecodeInstructionLength(*pui32CurrentToken); + const OPCODE_TYPE eOpcode = DecodeOpcodeType(*pui32CurrentToken); + + if (eOpcode == OPCODE_CUSTOMDATA) + { + ui32TokenLength = pui32CurrentToken[1]; + } + + pui32CurrentToken = pui32CurrentToken + ui32TokenLength; + + if (eOpcode == ePhaseOpcode) + { + ui32InstanceCount++; + } + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + + if (ui32InstanceCount) + { + psShader->asPhase[ui32Phase].pui32DeclCount = hlslcc_malloc(sizeof(uint32_t) * ui32InstanceCount); + psShader->asPhase[ui32Phase].ppsDecl = hlslcc_malloc(sizeof(Declaration*) * ui32InstanceCount); + psShader->asPhase[ui32Phase].pui32DeclCount[0] = 0; + + psShader->asPhase[ui32Phase].pui32InstCount = hlslcc_malloc(sizeof(uint32_t) * ui32InstanceCount); + psShader->asPhase[ui32Phase].ppsInst = hlslcc_malloc(sizeof(Instruction*) * ui32InstanceCount); + psShader->asPhase[ui32Phase].pui32InstCount[0] = 0; + } +} + +const uint32_t* DecodeHullShader(const uint32_t* pui32Tokens, ShaderData* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = psShader->ui32ShaderLength; + Declaration* psDecl; + + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32ShaderLength); + + psShader->asPhase[HS_GLOBAL_DECL].ppsInst = 0; + psShader->asPhase[HS_GLOBAL_DECL].ppsDecl = hlslcc_malloc(sizeof(Declaration*)); + psShader->asPhase[HS_GLOBAL_DECL].ppsDecl[0] = psDecl; + psShader->asPhase[HS_GLOBAL_DECL].pui32DeclCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->asPhase[HS_GLOBAL_DECL].pui32DeclCount[0] = 0; + psShader->asPhase[HS_GLOBAL_DECL].ui32InstanceCount = 1; + + AllocateHullPhaseArrays(pui32Tokens, psShader, HS_CTRL_POINT_PHASE, OPCODE_HS_CONTROL_POINT_PHASE); + AllocateHullPhaseArrays(pui32Tokens, psShader, HS_FORK_PHASE, OPCODE_HS_FORK_PHASE); + AllocateHullPhaseArrays(pui32Tokens, psShader, HS_JOIN_PHASE, OPCODE_HS_JOIN_PHASE); + + //Keep going until we have done all phases or the end of the shader. + while (1) + { + const uint32_t* pui32Result = DecodeDeclaration(psShader, pui32CurrentToken, psDecl); + + if (pui32Result) + { + pui32CurrentToken = pui32Result; + + if (psDecl->eOpcode == OPCODE_HS_CONTROL_POINT_PHASE) + { + pui32CurrentToken = DecodeShaderPhase(pui32CurrentToken, psShader, HS_CTRL_POINT_PHASE); + } + else if (psDecl->eOpcode == OPCODE_HS_FORK_PHASE) + { + pui32CurrentToken = DecodeShaderPhase(pui32CurrentToken, psShader, HS_FORK_PHASE); + } + else if (psDecl->eOpcode == OPCODE_HS_JOIN_PHASE) + { + pui32CurrentToken = DecodeShaderPhase(pui32CurrentToken, psShader, HS_JOIN_PHASE); + } + else + { + psDecl++; + psShader->asPhase[HS_GLOBAL_DECL].pui32DeclCount[0]++; + } + + if (pui32CurrentToken >= (psShader->pui32FirstToken + ui32ShaderLength)) + { + break; + } + } + else + { + break; + } + } + + return pui32CurrentToken; +} + +void Decode(const uint32_t* pui32Tokens, ShaderData* psShader) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + const uint32_t ui32ShaderLength = pui32Tokens[1]; + + psShader->ui32MajorVersion = DecodeProgramMajorVersion(*pui32CurrentToken); + psShader->ui32MinorVersion = DecodeProgramMinorVersion(*pui32CurrentToken); + psShader->eShaderType = DecodeShaderType(*pui32CurrentToken); + + pui32CurrentToken++;//Move to shader length + psShader->ui32ShaderLength = ui32ShaderLength; + pui32CurrentToken++;//Move to after shader length (usually a declaration) + + psShader->pui32FirstToken = pui32Tokens; + + if (psShader->eShaderType == HULL_SHADER) + { + pui32CurrentToken = DecodeHullShader(pui32CurrentToken, psShader); + return; + } + + psShader->asPhase[MAIN_PHASE].ui32InstanceCount = 0; + psShader->asPhase[MAIN_PHASE].pui32DeclCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->asPhase[MAIN_PHASE].ppsDecl = hlslcc_malloc(sizeof(Declaration*)); + psShader->asPhase[MAIN_PHASE].pui32DeclCount[0] = 0; + + psShader->asPhase[MAIN_PHASE].pui32InstCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->asPhase[MAIN_PHASE].ppsInst = hlslcc_malloc(sizeof(Instruction*)); + psShader->asPhase[MAIN_PHASE].pui32InstCount[0] = 0; + + DecodeShaderPhase(pui32CurrentToken, psShader, MAIN_PHASE); +} + +ShaderData* DecodeDXBC(uint32_t* data) +{ + ShaderData* psShader; + DXBCContainerHeader* header = (DXBCContainerHeader*)data; + uint32_t i; + uint32_t chunkCount; + uint32_t* chunkOffsets; + ReflectionChunks refChunks; + uint32_t* shaderChunk = 0; + + if (header->fourcc != FOURCC_DXBC) + { + //Could be SM1/2/3. If the shader type token + //looks valid then we continue + uint32_t type = DecodeShaderTypeDX9(data[0]); + + if (type != INVALID_SHADER) + { + return DecodeDX9BC(data); + } + return 0; + } + + refChunks.pui32Inputs = NULL; + refChunks.pui32Interfaces = NULL; + refChunks.pui32Outputs = NULL; + refChunks.pui32Resources = NULL; + refChunks.pui32Inputs11 = NULL; + refChunks.pui32Outputs11 = NULL; + refChunks.pui32OutputsWithStreams = NULL; + refChunks.pui32PatchConstants = NULL; + refChunks.pui32Effects10Data = NULL; + + chunkOffsets = (uint32_t*)(header + 1); + + chunkCount = header->chunkCount; + + for (i = 0; i < chunkCount; ++i) + { + uint32_t offset = chunkOffsets[i]; + + DXBCChunkHeader* chunk = (DXBCChunkHeader*)((char*)data + offset); + + switch (chunk->fourcc) + { + case FOURCC_ISGN: + { + refChunks.pui32Inputs = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_ISG1: + { + refChunks.pui32Inputs11 = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_RDEF: + { + refChunks.pui32Resources = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_IFCE: + { + refChunks.pui32Interfaces = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_OSGN: + { + refChunks.pui32Outputs = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_OSG1: + { + refChunks.pui32Outputs11 = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_OSG5: + { + refChunks.pui32OutputsWithStreams = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_SHDR: + case FOURCC_SHEX: + { + shaderChunk = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_PSGN: + { + refChunks.pui32PatchConstants = (uint32_t*)(chunk + 1); + break; + } + case FOURCC_FX10: + { + refChunks.pui32Effects10Data = (uint32_t*)(chunk + 1); + break; + } + default: + { + break; + } + } + } + + if (shaderChunk) + { + uint32_t ui32MajorVersion; + uint32_t ui32MinorVersion; + + psShader = hlslcc_calloc(1, sizeof(ShaderData)); + + ui32MajorVersion = DecodeProgramMajorVersion(*shaderChunk); + ui32MinorVersion = DecodeProgramMinorVersion(*shaderChunk); + + LoadShaderInfo(ui32MajorVersion, + ui32MinorVersion, + &refChunks, + &psShader->sInfo); + + Decode(shaderChunk, psShader); + + return psShader; + } + + return 0; +} + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/decodeDX9.c b/Code/Tools/HLSLCrossCompilerMETAL/src/decodeDX9.c new file mode 100644 index 0000000000..f33c5b4b0e --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/decodeDX9.c @@ -0,0 +1,1133 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/debug.h" +#include "internal_includes/decode.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/reflect.h" +#include "internal_includes/structs.h" +#include "internal_includes/tokens.h" +#include "stdio.h" +#include "stdlib.h" + +#define FOURCC(a, b, c, d) ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24)) +enum +{ + FOURCC_CTAB = FOURCC('C', 'T', 'A', 'B') +}; // Constant table + +#ifdef _DEBUG +static uint64_t dx9operandID = 0; +static uint64_t dx9instructionID = 0; +#endif + +static uint32_t aui32ImmediateConst[256]; +static uint32_t ui32MaxTemp = 0; + +uint32_t DX9_DECODE_OPERAND_IS_SRC = 0x1; +uint32_t DX9_DECODE_OPERAND_IS_DEST = 0x2; +uint32_t DX9_DECODE_OPERAND_IS_DECL = 0x4; + +uint32_t DX9_DECODE_OPERAND_IS_CONST = 0x8; +uint32_t DX9_DECODE_OPERAND_IS_ICONST = 0x10; +uint32_t DX9_DECODE_OPERAND_IS_BCONST = 0x20; + +#define MAX_INPUTS 64 + +static DECLUSAGE_DX9 aeInputUsage[MAX_INPUTS]; +static uint32_t aui32InputUsageIndex[MAX_INPUTS]; + +static void DecodeOperandDX9(const ShaderData* psShader, const uint32_t ui32Token, const uint32_t ui32Token1, uint32_t ui32Flags, Operand* psOperand) +{ + const uint32_t ui32RegNum = DecodeOperandRegisterNumberDX9(ui32Token); + const uint32_t ui32RegType = DecodeOperandTypeDX9(ui32Token); + const uint32_t bRelativeAddr = DecodeOperandIsRelativeAddressModeDX9(ui32Token); + + const uint32_t ui32WriteMask = DecodeDestWriteMaskDX9(ui32Token); + const uint32_t ui32Swizzle = DecodeOperandSwizzleDX9(ui32Token); + + SHADER_VARIABLE_TYPE ConstType; + + psOperand->ui32RegisterNumber = ui32RegNum; + + psOperand->iNumComponents = 4; + +#ifdef _DEBUG + psOperand->id = dx9operandID++; +#endif + + psOperand->iWriteMaskEnabled = 0; + psOperand->iGSInput = 0; + psOperand->iExtended = 0; + psOperand->psSubOperand[0] = 0; + psOperand->psSubOperand[1] = 0; + psOperand->psSubOperand[2] = 0; + + psOperand->iIndexDims = INDEX_0D; + + psOperand->iIntegerImmediate = 0; + + psOperand->pszSpecialName[0] = '\0'; + + psOperand->eModifier = OPERAND_MODIFIER_NONE; + if (ui32Flags & DX9_DECODE_OPERAND_IS_SRC) + { + uint32_t ui32Modifier = DecodeSrcModifierDX9(ui32Token); + + switch (ui32Modifier) + { + case SRCMOD_DX9_NONE: + { + break; + } + case SRCMOD_DX9_NEG: + { + psOperand->eModifier = OPERAND_MODIFIER_NEG; + break; + } + case SRCMOD_DX9_ABS: + { + psOperand->eModifier = OPERAND_MODIFIER_ABS; + break; + } + case SRCMOD_DX9_ABSNEG: + { + psOperand->eModifier = OPERAND_MODIFIER_ABSNEG; + break; + } + default: + { + ASSERT(0); + break; + } + } + } + + if ((ui32Flags & DX9_DECODE_OPERAND_IS_DECL) == 0) + { + if (ui32Flags & DX9_DECODE_OPERAND_IS_DEST) + { + if (ui32WriteMask != DX9_WRITEMASK_ALL) + { + psOperand->iWriteMaskEnabled = 1; + psOperand->eSelMode = OPERAND_4_COMPONENT_MASK_MODE; + + if (ui32WriteMask & DX9_WRITEMASK_0) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_X; + } + if (ui32WriteMask & DX9_WRITEMASK_1) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_Y; + } + if (ui32WriteMask & DX9_WRITEMASK_2) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_Z; + } + if (ui32WriteMask & DX9_WRITEMASK_3) + { + psOperand->ui32CompMask |= OPERAND_4_COMPONENT_MASK_W; + } + } + } + else if (ui32Swizzle != NO_SWIZZLE_DX9) + { + uint32_t component; + + psOperand->iWriteMaskEnabled = 1; + psOperand->eSelMode = OPERAND_4_COMPONENT_SWIZZLE_MODE; + + psOperand->ui32Swizzle = 1; + + /* Add the swizzle */ + if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(0)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_X; + } + else if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(1)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_Y; + } + else if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(2)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_Z; + } + else if (ui32Swizzle == REPLICATE_SWIZZLE_DX9(3)) + { + psOperand->eSelMode = OPERAND_4_COMPONENT_SELECT_1_MODE; + psOperand->aui32Swizzle[0] = OPERAND_4_COMPONENT_W; + } + else + { + for (component = 0; component < 4; component++) + { + uint32_t ui32CompSwiz = ui32Swizzle & (3 << (DX9_SWIZZLE_SHIFT + (component * 2))); + ui32CompSwiz >>= (DX9_SWIZZLE_SHIFT + (component * 2)); + + if (ui32CompSwiz == 0) + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_X; + } + else if (ui32CompSwiz == 1) + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_Y; + } + else if (ui32CompSwiz == 2) + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_Z; + } + else + { + psOperand->aui32Swizzle[component] = OPERAND_4_COMPONENT_W; + } + } + } + } + + if (bRelativeAddr) + { + psOperand->psSubOperand[0] = hlslcc_malloc(sizeof(Operand)); + DecodeOperandDX9(psShader, ui32Token1, 0, ui32Flags, psOperand->psSubOperand[0]); + + psOperand->iIndexDims = INDEX_1D; + + psOperand->eIndexRep[0] = OPERAND_INDEX_RELATIVE; + + psOperand->aui32ArraySizes[0] = 0; + } + } + + if (ui32RegType == OPERAND_TYPE_DX9_CONSTBOOL) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_BCONST; + ConstType = SVT_BOOL; + } + else if (ui32RegType == OPERAND_TYPE_DX9_CONSTINT) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_ICONST; + ConstType = SVT_INT; + } + else if (ui32RegType == OPERAND_TYPE_DX9_CONST) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_CONST; + ConstType = SVT_FLOAT; + } + + switch (ui32RegType) + { + case OPERAND_TYPE_DX9_TEMP: + { + psOperand->eType = OPERAND_TYPE_TEMP; + + if (ui32MaxTemp < ui32RegNum + 1) + { + ui32MaxTemp = ui32RegNum + 1; + } + break; + } + case OPERAND_TYPE_DX9_INPUT: + { + psOperand->eType = OPERAND_TYPE_INPUT; + + ASSERT(ui32RegNum < MAX_INPUTS); + + if (psShader->eShaderType == PIXEL_SHADER) + { + if (aeInputUsage[ui32RegNum] == DECLUSAGE_TEXCOORD) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_TEXCOORD; + psOperand->ui32RegisterNumber = aui32InputUsageIndex[ui32RegNum]; + } + else + // 0 = base colour, 1 = offset colour. + if (ui32RegNum == 0) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTBASECOLOUR; + } + else + { + ASSERT(ui32RegNum == 1); + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR; + } + } + break; + } + // Same value as OPERAND_TYPE_DX9_TEXCRDOUT + // OPERAND_TYPE_DX9_TEXCRDOUT is the pre-SM3 equivalent + case OPERAND_TYPE_DX9_OUTPUT: + { + psOperand->eType = OPERAND_TYPE_OUTPUT; + + if (psShader->eShaderType == VERTEX_SHADER) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_TEXCOORD; + } + break; + } + case OPERAND_TYPE_DX9_RASTOUT: + { + // RegNum: + // 0=POSIION + // 1=FOG + // 2=POINTSIZE + psOperand->eType = OPERAND_TYPE_OUTPUT; + switch (ui32RegNum) + { + case 0: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_POSITION; + break; + } + case 1: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_FOG; + break; + } + case 2: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_POINTSIZE; + psOperand->iNumComponents = 1; + break; + } + } + break; + } + case OPERAND_TYPE_DX9_ATTROUT: + { + ASSERT(psShader->eShaderType == VERTEX_SHADER); + + psOperand->eType = OPERAND_TYPE_OUTPUT; + + // 0 = base colour, 1 = offset colour. + if (ui32RegNum == 0) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTBASECOLOUR; + } + else + { + ASSERT(ui32RegNum == 1); + psOperand->eType = OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR; + } + + break; + } + case OPERAND_TYPE_DX9_COLOROUT: + { + ASSERT(psShader->eShaderType == PIXEL_SHADER); + psOperand->eType = OPERAND_TYPE_OUTPUT; + break; + } + case OPERAND_TYPE_DX9_CONSTBOOL: + case OPERAND_TYPE_DX9_CONSTINT: + case OPERAND_TYPE_DX9_CONST: + { + // c# = constant float + // i# = constant int + // b# = constant bool + + // c0 might be an immediate while i0 is in the constant buffer + if (aui32ImmediateConst[ui32RegNum] & ui32Flags) + { + if (ConstType != SVT_FLOAT) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_IMMCONSTINT; + } + else + { + psOperand->eType = OPERAND_TYPE_SPECIAL_IMMCONST; + } + } + else + { + psOperand->eType = OPERAND_TYPE_CONSTANT_BUFFER; + psOperand->aui32ArraySizes[1] = psOperand->ui32RegisterNumber; + } + break; + } + case OPERAND_TYPE_DX9_ADDR: + { + // Vertex shader: address register (only have one of these) + // Pixel shader: texture coordinate register (a few of these) + if (psShader->eShaderType == PIXEL_SHADER) + { + psOperand->eType = OPERAND_TYPE_SPECIAL_TEXCOORD; + } + else + { + psOperand->eType = OPERAND_TYPE_SPECIAL_ADDRESS; + } + break; + } + case OPERAND_TYPE_DX9_SAMPLER: + { + psOperand->eType = OPERAND_TYPE_RESOURCE; + break; + } + case OPERAND_TYPE_DX9_LOOP: + { + psOperand->eType = OPERAND_TYPE_SPECIAL_LOOPCOUNTER; + break; + } + default: + { + ASSERT(0); + break; + } + } +} + +static void DeclareNumTemps(ShaderData* psShader, const uint32_t ui32NumTemps, Declaration* psDecl) +{ + (void)psShader; + + psDecl->eOpcode = OPCODE_DCL_TEMPS; + psDecl->value.ui32NumTemps = ui32NumTemps; +} + +static void SetupRegisterUsage(const ShaderData* psShader, const uint32_t ui32Token0, const uint32_t ui32Token1) +{ + (void)psShader; + + DECLUSAGE_DX9 eUsage = DecodeUsageDX9(ui32Token0); + uint32_t ui32UsageIndex = DecodeUsageIndexDX9(ui32Token0); + uint32_t ui32RegNum = DecodeOperandRegisterNumberDX9(ui32Token1); + uint32_t ui32RegType = DecodeOperandTypeDX9(ui32Token1); + + if (ui32RegType == OPERAND_TYPE_DX9_INPUT) + { + ASSERT(ui32RegNum < MAX_INPUTS); + aeInputUsage[ui32RegNum] = eUsage; + aui32InputUsageIndex[ui32RegNum] = ui32UsageIndex; + } +} + +// Declaring one constant from a constant buffer will cause all constants in the buffer decalared. +// In dx9 there is only one constant buffer per shader. +static void DeclareConstantBuffer(const ShaderData* psShader, Declaration* psDecl) +{ + // Pick any constant register in the table. Might not start at c0 (e.g. when register(cX) is used). + uint32_t ui32RegNum = psShader->sInfo.psConstantBuffers->asVars[0].ui32StartOffset / 16; + OPERAND_TYPE_DX9 ui32RegType = OPERAND_TYPE_DX9_CONST; + + if (psShader->sInfo.psConstantBuffers->asVars[0].sType.Type == SVT_INT) + { + ui32RegType = OPERAND_TYPE_DX9_CONSTINT; + } + else if (psShader->sInfo.psConstantBuffers->asVars[0].sType.Type == SVT_BOOL) + { + ui32RegType = OPERAND_TYPE_DX9_CONSTBOOL; + } + + if (psShader->eShaderType == VERTEX_SHADER) + { + psDecl->eOpcode = OPCODE_DCL_INPUT; + } + else + { + psDecl->eOpcode = OPCODE_DCL_INPUT_PS; + } + psDecl->ui32NumOperands = 1; + + DecodeOperandDX9(psShader, CreateOperandTokenDX9(ui32RegNum, ui32RegType), 0, DX9_DECODE_OPERAND_IS_DECL, &psDecl->asOperands[0]); + + ASSERT(psDecl->asOperands[0].eType == OPERAND_TYPE_CONSTANT_BUFFER); + + psDecl->eOpcode = OPCODE_DCL_CONSTANT_BUFFER; + + ASSERT(psShader->sInfo.ui32NumConstantBuffers); + + psDecl->asOperands[0].aui32ArraySizes[0] = 0; // Const buffer index + psDecl->asOperands[0].aui32ArraySizes[1] = psShader->sInfo.psConstantBuffers[0].ui32TotalSizeInBytes / 16; // Number of vec4 constants. +} + +static void DecodeDeclarationDX9(const ShaderData* psShader, const uint32_t ui32Token0, const uint32_t ui32Token1, Declaration* psDecl) +{ + /*uint32_t ui32UsageIndex = DecodeUsageIndexDX9(ui32Token0);*/ + uint32_t ui32RegType = DecodeOperandTypeDX9(ui32Token1); + + if (psShader->eShaderType == VERTEX_SHADER) + { + psDecl->eOpcode = OPCODE_DCL_INPUT; + } + else + { + psDecl->eOpcode = OPCODE_DCL_INPUT_PS; + } + psDecl->ui32NumOperands = 1; + DecodeOperandDX9(psShader, ui32Token1, 0, DX9_DECODE_OPERAND_IS_DECL, &psDecl->asOperands[0]); + + if (ui32RegType == OPERAND_TYPE_DX9_SAMPLER) + { + const RESOURCE_DIMENSION eResDim = DecodeTextureTypeMaskDX9(ui32Token0); + psDecl->value.eResourceDimension = eResDim; + psDecl->ui32IsShadowTex = 0; + psDecl->eOpcode = OPCODE_DCL_RESOURCE; + } + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_OUTPUT) + { + psDecl->eOpcode = OPCODE_DCL_OUTPUT; + + if (psDecl->asOperands[0].ui32RegisterNumber == 0 && psShader->eShaderType == VERTEX_SHADER) + { + psDecl->eOpcode = OPCODE_DCL_OUTPUT_SIV; + // gl_Position + psDecl->asOperands[0].eSpecialName = NAME_POSITION; + } + } + else if (psDecl->asOperands[0].eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + psDecl->eOpcode = OPCODE_DCL_CONSTANT_BUFFER; + + ASSERT(psShader->sInfo.ui32NumConstantBuffers); + + psDecl->asOperands[0].aui32ArraySizes[0] = 0; // Const buffer index + psDecl->asOperands[0].aui32ArraySizes[1] = psShader->sInfo.psConstantBuffers[0].ui32TotalSizeInBytes / 16; // Number of vec4 constants. + } +} + +static void DefineDX9(ShaderData* psShader, + const uint32_t ui32RegNum, + const uint32_t ui32Flags, + const uint32_t c0, + const uint32_t c1, + const uint32_t c2, + const uint32_t c3, + Declaration* psDecl) +{ + (void)psShader; + (void)psDecl; + + psDecl->eOpcode = OPCODE_SPECIAL_DCL_IMMCONST; + psDecl->ui32NumOperands = 2; + + memset(&psDecl->asOperands[0], 0, sizeof(Operand)); + psDecl->asOperands[0].eType = OPERAND_TYPE_SPECIAL_IMMCONST; + + psDecl->asOperands[0].ui32RegisterNumber = ui32RegNum; + + if (ui32Flags & (DX9_DECODE_OPERAND_IS_ICONST | DX9_DECODE_OPERAND_IS_BCONST)) + { + psDecl->asOperands[0].eType = OPERAND_TYPE_SPECIAL_IMMCONSTINT; + } + + aui32ImmediateConst[ui32RegNum] |= ui32Flags; + + memset(&psDecl->asOperands[1], 0, sizeof(Operand)); + psDecl->asOperands[1].eType = OPERAND_TYPE_IMMEDIATE32; + psDecl->asOperands[1].iNumComponents = 4; + psDecl->asOperands[1].iIntegerImmediate = (ui32Flags & (DX9_DECODE_OPERAND_IS_ICONST | DX9_DECODE_OPERAND_IS_BCONST)) ? 1 : 0; + psDecl->asOperands[1].afImmediates[0] = *((float*)&c0); + psDecl->asOperands[1].afImmediates[1] = *((float*)&c1); + psDecl->asOperands[1].afImmediates[2] = *((float*)&c2); + psDecl->asOperands[1].afImmediates[3] = *((float*)&c3); +} + +static void CreateD3D10Instruction(ShaderData* psShader, + Instruction* psInst, + const OPCODE_TYPE eType, + const uint32_t bHasDest, + const uint32_t ui32SrcCount, + const uint32_t* pui32Tokens) +{ + uint32_t ui32Src; + uint32_t ui32Offset = 1; + + memset(psInst, 0, sizeof(Instruction)); + +#ifdef _DEBUG + psInst->id = dx9instructionID++; +#endif + + psInst->eOpcode = eType; + psInst->ui32NumOperands = ui32SrcCount; + + if (bHasDest) + { + ++psInst->ui32NumOperands; + + DecodeOperandDX9(psShader, pui32Tokens[ui32Offset], pui32Tokens[ui32Offset + 1], DX9_DECODE_OPERAND_IS_DEST, &psInst->asOperands[0]); + + if (DecodeDestModifierDX9(pui32Tokens[ui32Offset]) & DESTMOD_DX9_SATURATE) + { + psInst->bSaturate = 1; + } + + ui32Offset++; + psInst->ui32FirstSrc = 1; + } + + for (ui32Src = 0; ui32Src < ui32SrcCount; ++ui32Src) + { + DecodeOperandDX9(psShader, pui32Tokens[ui32Offset], pui32Tokens[ui32Offset + 1], DX9_DECODE_OPERAND_IS_SRC, &psInst->asOperands[bHasDest + ui32Src]); + + ui32Offset++; + } +} + +ShaderData* DecodeDX9BC(const uint32_t* pui32Tokens) +{ + const uint32_t* pui32CurrentToken = pui32Tokens; + uint32_t ui32NumInstructions = 0; + uint32_t ui32NumDeclarations = 0; + Instruction* psInst; + Declaration* psDecl; + uint32_t decl, inst; + uint32_t bDeclareConstantTable = 0; + ShaderData* psShader = hlslcc_calloc(1, sizeof(ShaderData)); + + memset(aui32ImmediateConst, 0, 256); + + psShader->ui32MajorVersion = DecodeProgramMajorVersionDX9(*pui32CurrentToken); + psShader->ui32MinorVersion = DecodeProgramMinorVersionDX9(*pui32CurrentToken); + psShader->eShaderType = DecodeShaderTypeDX9(*pui32CurrentToken); + + pui32CurrentToken++; + + // Work out how many instructions and declarations we need to allocate memory for. + while (1) + { + OPCODE_TYPE_DX9 eOpcode = DecodeOpcodeTypeDX9(pui32CurrentToken[0]); + uint32_t ui32InstLen = DecodeInstructionLengthDX9(pui32CurrentToken[0]); + + if (eOpcode == OPCODE_DX9_END) + { + // SM4+ always end with RET. + // Insert a RET instruction on END to + // replicate this behaviour. + ++ui32NumInstructions; + break; + } + else if (eOpcode == OPCODE_DX9_COMMENT) + { + ui32InstLen = DecodeCommentLengthDX9(pui32CurrentToken[0]); + if (pui32CurrentToken[1] == FOURCC_CTAB) + { + LoadD3D9ConstantTable((char*)(&pui32CurrentToken[2]), &psShader->sInfo); + + ASSERT(psShader->sInfo.ui32NumConstantBuffers); + + if (psShader->sInfo.psConstantBuffers[0].ui32NumVars) + { + ++ui32NumDeclarations; + bDeclareConstantTable = 1; + } + } + } + else if ((eOpcode == OPCODE_DX9_DEF) || (eOpcode == OPCODE_DX9_DEFI) || (eOpcode == OPCODE_DX9_DEFB)) + { + ++ui32NumDeclarations; + } + else if (eOpcode == OPCODE_DX9_DCL) + { + const OPERAND_TYPE_DX9 eType = DecodeOperandTypeDX9(pui32CurrentToken[2]); + uint32_t ignoreDCL = 0; + + // Inputs and outputs are declared in AddVersionDependentCode + if (psShader->eShaderType == PIXEL_SHADER && (OPERAND_TYPE_DX9_CONST != eType && OPERAND_TYPE_DX9_SAMPLER != eType)) + { + ignoreDCL = 1; + } + if (!ignoreDCL) + { + ++ui32NumDeclarations; + } + } + else + { + switch (eOpcode) + { + case OPCODE_DX9_NRM: + { + // Emulate with dp4 and rsq + ui32NumInstructions += 2; + break; + } + default: + { + ++ui32NumInstructions; + break; + } + } + } + + pui32CurrentToken += ui32InstLen + 1; + } + + psInst = hlslcc_malloc(sizeof(Instruction) * ui32NumInstructions); + psShader->asPhase[MAIN_PHASE].ui32InstanceCount = 1; + psShader->asPhase[MAIN_PHASE].ppsInst = hlslcc_malloc(sizeof(Instruction*)); + psShader->asPhase[MAIN_PHASE].ppsInst[0] = psInst; + psShader->asPhase[MAIN_PHASE].pui32InstCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->asPhase[MAIN_PHASE].pui32InstCount[0] = ui32NumInstructions; + + if (psShader->eShaderType == VERTEX_SHADER) + { + // Declare gl_Position. vs_3_0 does declare it, SM1/2 do not + ui32NumDeclarations++; + } + + // For declaring temps. + ui32NumDeclarations++; + + psDecl = hlslcc_malloc(sizeof(Declaration) * ui32NumDeclarations); + psShader->asPhase[MAIN_PHASE].ppsDecl = hlslcc_malloc(sizeof(Declaration*)); + psShader->asPhase[MAIN_PHASE].ppsDecl[0] = psDecl; + psShader->asPhase[MAIN_PHASE].pui32DeclCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->asPhase[MAIN_PHASE].pui32DeclCount[0] = ui32NumDeclarations; + + pui32CurrentToken = pui32Tokens + 1; + + inst = 0; + decl = 0; + while (1) + { + OPCODE_TYPE_DX9 eOpcode = DecodeOpcodeTypeDX9(pui32CurrentToken[0]); + uint32_t ui32InstLen = DecodeInstructionLengthDX9(pui32CurrentToken[0]); + + if (eOpcode == OPCODE_DX9_END) + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RET, 0, 0, pui32CurrentToken); + inst++; + break; + } + else if (eOpcode == OPCODE_DX9_COMMENT) + { + ui32InstLen = DecodeCommentLengthDX9(pui32CurrentToken[0]); + } + else if (eOpcode == OPCODE_DX9_DCL) + { + const OPERAND_TYPE_DX9 eType = DecodeOperandTypeDX9(pui32CurrentToken[2]); + uint32_t ignoreDCL = 0; + // Inputs and outputs are declared in AddVersionDependentCode + if (psShader->eShaderType == PIXEL_SHADER && (OPERAND_TYPE_DX9_CONST != eType && OPERAND_TYPE_DX9_SAMPLER != eType)) + { + ignoreDCL = 1; + } + + SetupRegisterUsage(psShader, pui32CurrentToken[1], pui32CurrentToken[2]); + + if (!ignoreDCL) + { + DecodeDeclarationDX9(psShader, pui32CurrentToken[1], pui32CurrentToken[2], &psDecl[decl]); + decl++; + } + } + else if ((eOpcode == OPCODE_DX9_DEF) || (eOpcode == OPCODE_DX9_DEFI) || (eOpcode == OPCODE_DX9_DEFB)) + { + const uint32_t ui32Const0 = *(pui32CurrentToken + 2); + const uint32_t ui32Const1 = *(pui32CurrentToken + 3); + const uint32_t ui32Const2 = *(pui32CurrentToken + 4); + const uint32_t ui32Const3 = *(pui32CurrentToken + 5); + uint32_t ui32Flags = 0; + + if (eOpcode == OPCODE_DX9_DEF) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_CONST; + } + else if (eOpcode == OPCODE_DX9_DEFI) + { + ui32Flags |= DX9_DECODE_OPERAND_IS_ICONST; + } + else + { + ui32Flags |= DX9_DECODE_OPERAND_IS_BCONST; + } + + DefineDX9(psShader, DecodeOperandRegisterNumberDX9(pui32CurrentToken[1]), ui32Flags, ui32Const0, ui32Const1, ui32Const2, ui32Const3, &psDecl[decl]); + decl++; + } + else + { + switch (eOpcode) + { + case OPCODE_DX9_MOV: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MOV, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_LIT: + { + /*Dest.x = 1 + Dest.y = (Src0.x > 0) ? Src0.x : 0 + Dest.z = (Src0.x > 0 && Src0.y > 0) ? pow(Src0.y, Src0.w) : 0 + Dest.w = 1 + */ + ASSERT(0); + break; + } + case OPCODE_DX9_ADD: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ADD, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_SUB: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ADD, 1, 2, pui32CurrentToken); + ASSERT(psInst[inst].asOperands[2].eModifier == OPERAND_MODIFIER_NONE); + psInst[inst].asOperands[2].eModifier = OPERAND_MODIFIER_NEG; + break; + } + case OPCODE_DX9_MAD: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MAD, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_MUL: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MUL, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_RCP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RCP, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_RSQ: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RSQ, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_DP3: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP3, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_DP4: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP4, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_MIN: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MIN, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_MAX: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MAX, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_SLT: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LT, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_SGE: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_GE, 1, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_EXP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_EXP, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_LOG: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LOG, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_NRM: + { + // Convert NRM RESULT, SRCA into: + // dp4 RESULT, SRCA, SRCA + // rsq RESULT, RESULT + + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP4, 1, 1, pui32CurrentToken); + memcpy(&psInst[inst].asOperands[2], &psInst[inst].asOperands[1], sizeof(Operand)); + psInst[inst].ui32NumOperands++; + ++inst; + + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RSQ, 0, 0, pui32CurrentToken); + memcpy(&psInst[inst].asOperands[0], &psInst[inst - 1].asOperands[0], sizeof(Operand)); + memcpy(&psInst[inst].asOperands[1], &psInst[inst - 1].asOperands[0], sizeof(Operand)); + psInst[inst].ui32NumOperands++; + psInst[inst].ui32NumOperands++; + break; + } + case OPCODE_DX9_SINCOS: + { + // Before SM3, SINCOS has 2 extra constant sources -D3DSINCOSCONST1 and D3DSINCOSCONST2. + // Ignore them. + + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SINCOS, 1, 1, pui32CurrentToken); + // Pre-SM4: + // If the write mask is .x: dest.x = cos( V ) + // If the write mask is .y: dest.y = sin( V ) + // If the write mask is .xy: + // dest.x = cos( V ) + // dest.y = sin( V ) + + // SM4+ + // destSin destCos Angle + + psInst[inst].ui32NumOperands = 3; + + // Set the angle + memcpy(&psInst[inst].asOperands[2], &psInst[inst].asOperands[1], sizeof(Operand)); + + // Set the cosine dest + memcpy(&psInst[inst].asOperands[1], &psInst[inst].asOperands[0], sizeof(Operand)); + + // Set write masks + psInst[inst].asOperands[0].ui32CompMask &= ~OPERAND_4_COMPONENT_MASK_Y; + if (psInst[inst].asOperands[0].ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + // Need cosine + } + else + { + psInst[inst].asOperands[0].eType = OPERAND_TYPE_NULL; + } + psInst[inst].asOperands[1].ui32CompMask &= ~OPERAND_4_COMPONENT_MASK_X; + if (psInst[inst].asOperands[1].ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + // Need sine + } + else + { + psInst[inst].asOperands[1].eType = OPERAND_TYPE_NULL; + } + + break; + } + case OPCODE_DX9_FRC: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_FRC, 1, 1, pui32CurrentToken); + break; + } + + case OPCODE_DX9_MOVA: + { + // MOVA preforms RoundToNearest on the src data. + // The only rounding functions available in all GLSL version are ceil and floor. + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ROUND_NI, 1, 1, pui32CurrentToken); + break; + } + + case OPCODE_DX9_TEX: + { + // texld r0, t0, s0 + // srcAddress[.swizzle], srcResource[.swizzle], srcSampler + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SAMPLE, 1, 2, pui32CurrentToken); + psInst[inst].asOperands[2].ui32RegisterNumber = 0; + + break; + } + case OPCODE_DX9_TEXLDL: + { + // texld r0, t0, s0 + // srcAddress[.swizzle], srcResource[.swizzle], srcSampler + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SAMPLE_L, 1, 2, pui32CurrentToken); + psInst[inst].asOperands[2].ui32RegisterNumber = 0; + + // Lod comes from fourth coordinate of address. + memcpy(&psInst[inst].asOperands[4], &psInst[inst].asOperands[1], sizeof(Operand)); + + psInst[inst].ui32NumOperands = 5; + + break; + } + + case OPCODE_DX9_IF: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_IF, 0, 1, pui32CurrentToken); + psInst[inst].eDX9TestType = D3DSPC_BOOLEAN; + break; + } + + case OPCODE_DX9_IFC: + { + const COMPARISON_DX9 eCmpOp = DecodeComparisonDX9(pui32CurrentToken[0]); + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_IF, 0, 2, pui32CurrentToken); + psInst[inst].eDX9TestType = eCmpOp; + break; + } + case OPCODE_DX9_ELSE: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ELSE, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_CMP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_MOVC, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_REP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_REP, 0, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_ENDREP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ENDREP, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_BREAKC: + { + const COMPARISON_DX9 eCmpOp = DecodeComparisonDX9(pui32CurrentToken[0]); + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_BREAKC, 0, 2, pui32CurrentToken); + psInst[inst].eDX9TestType = eCmpOp; + break; + } + + case OPCODE_DX9_DSX: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DERIV_RTX, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_DSY: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DERIV_RTY, 1, 1, pui32CurrentToken); + break; + } + case OPCODE_DX9_TEXKILL: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DISCARD, 1, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_TEXLDD: + { + // texldd, dst, src0, src1, src2, src3 + // srcAddress[.swizzle], srcResource[.swizzle], srcSampler, XGradient, YGradient + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_SAMPLE_D, 1, 4, pui32CurrentToken); + + // Move the gradients one slot up + memcpy(&psInst[inst].asOperands[5], &psInst[inst].asOperands[4], sizeof(Operand)); + memcpy(&psInst[inst].asOperands[4], &psInst[inst].asOperands[3], sizeof(Operand)); + + // Sampler register + psInst[inst].asOperands[3].ui32RegisterNumber = 0; + psInst[inst].ui32NumOperands = 6; + break; + } + case OPCODE_DX9_LRP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LRP, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_DP2ADD: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_DP2ADD, 1, 3, pui32CurrentToken); + break; + } + case OPCODE_DX9_POW: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_POW, 1, 2, pui32CurrentToken); + break; + } + + case OPCODE_DX9_DST: + case OPCODE_DX9_M4x4: + case OPCODE_DX9_M4x3: + case OPCODE_DX9_M3x4: + case OPCODE_DX9_M3x3: + case OPCODE_DX9_M3x2: + case OPCODE_DX9_CALL: + case OPCODE_DX9_CALLNZ: + case OPCODE_DX9_LABEL: + + case OPCODE_DX9_CRS: + case OPCODE_DX9_SGN: + case OPCODE_DX9_ABS: + + case OPCODE_DX9_TEXCOORD: + case OPCODE_DX9_TEXBEM: + case OPCODE_DX9_TEXBEML: + case OPCODE_DX9_TEXREG2AR: + case OPCODE_DX9_TEXREG2GB: + case OPCODE_DX9_TEXM3x2PAD: + case OPCODE_DX9_TEXM3x2TEX: + case OPCODE_DX9_TEXM3x3PAD: + case OPCODE_DX9_TEXM3x3TEX: + case OPCODE_DX9_TEXM3x3SPEC: + case OPCODE_DX9_TEXM3x3VSPEC: + case OPCODE_DX9_EXPP: + case OPCODE_DX9_LOGP: + case OPCODE_DX9_CND: + case OPCODE_DX9_TEXREG2RGB: + case OPCODE_DX9_TEXDP3TEX: + case OPCODE_DX9_TEXM3x2DEPTH: + case OPCODE_DX9_TEXDP3: + case OPCODE_DX9_TEXM3x3: + case OPCODE_DX9_TEXDEPTH: + case OPCODE_DX9_BEM: + case OPCODE_DX9_SETP: + case OPCODE_DX9_BREAKP: + { + ASSERT(0); + break; + } + case OPCODE_DX9_NOP: + case OPCODE_DX9_PHASE: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_NOP, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_LOOP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_LOOP, 0, 2, pui32CurrentToken); + break; + } + case OPCODE_DX9_RET: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_RET, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_ENDLOOP: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ENDLOOP, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_ENDIF: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_ENDIF, 0, 0, pui32CurrentToken); + break; + } + case OPCODE_DX9_BREAK: + { + CreateD3D10Instruction(psShader, &psInst[inst], OPCODE_BREAK, 0, 0, pui32CurrentToken); + break; + } + default: + { + ASSERT(0); + break; + } + } + + UpdateOperandReferences(psShader, &psInst[inst]); + + inst++; + } + + pui32CurrentToken += ui32InstLen + 1; + } + + DeclareNumTemps(psShader, ui32MaxTemp, &psDecl[decl]); + ++decl; + + if (psShader->eShaderType == VERTEX_SHADER) + { + // Declare gl_Position. vs_3_0 does declare it, SM1/2 do not + if (bDeclareConstantTable) + { + DecodeDeclarationDX9(psShader, 0, CreateOperandTokenDX9(0, OPERAND_TYPE_DX9_RASTOUT), &psDecl[decl + 1]); + } + else + { + DecodeDeclarationDX9(psShader, 0, CreateOperandTokenDX9(0, OPERAND_TYPE_DX9_RASTOUT), &psDecl[decl]); + } + } + + if (bDeclareConstantTable) + { + DeclareConstantBuffer(psShader, &psDecl[decl]); + } + + return psShader; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/debug.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/debug.h new file mode 100644 index 0000000000..5b071709bc --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/debug.h @@ -0,0 +1,21 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#ifdef _DEBUG +#include "assert.h" +#define ASSERT(expr) CustomAssert(expr) +static void CustomAssert(int expression) +{ + if(!expression) + { + assert(0); + } +} +#else +#define ASSERT(expr) +#endif + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/decode.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/decode.h new file mode 100644 index 0000000000..f0981cb15c --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/decode.h @@ -0,0 +1,18 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef DECODE_H +#define DECODE_H + +#include "internal_includes/structs.h" + +ShaderData* DecodeDXBC(uint32_t* data); + +//You don't need to call this directly because DecodeDXBC +//will call DecodeDX9BC if the shader looks +//like it is SM1/2/3. +ShaderData* DecodeDX9BC(const uint32_t* pui32Tokens); + +void UpdateOperandReferences(ShaderData* psShader, Instruction* psInst); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.c b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.c new file mode 100644 index 0000000000..57c86655b7 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.c @@ -0,0 +1,37 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include + +#ifdef __APPLE_CC__ + #include +#else +#include +#endif + +// Wrapping these functions since we are taking the address of them and the std functions are dllimport which produce +// warning C4232 +void* std_malloc(size_t size) +{ + return malloc(size); +} + +void* std_calloc(size_t num, size_t size) +{ + return calloc(num, size); +} + +void std_free(void* p) +{ + free(p); +} + +void* std_realloc(void* p, size_t size) +{ + return realloc(p, size); +} + +void* (*hlslcc_malloc)(size_t size) = std_malloc; +void* (*hlslcc_calloc)(size_t num,size_t size) = std_calloc; +void (*hlslcc_free)(void *p) = std_free; +void* (*hlslcc_realloc)(void *p,size_t size) = std_realloc; diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.h new file mode 100644 index 0000000000..493aa1fe1e --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/hlslcc_malloc.h @@ -0,0 +1,15 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef __HLSCC_MALLOC_H +#define __HLSCC_MALLOC_H + +extern void* (*hlslcc_malloc)(size_t size); +extern void* (*hlslcc_calloc)(size_t num,size_t size); +extern void (*hlslcc_free)(void *p); +extern void* (*hlslcc_realloc)(void *p,size_t size); + +#define bstr__alloc hlslcc_malloc +#define bstr__free hlslcc_free +#define bstr__realloc hlslcc_realloc +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/languages.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/languages.h new file mode 100644 index 0000000000..35d7a9b125 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/languages.h @@ -0,0 +1,213 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef LANGUAGES_H +#define LANGUAGES_H + +#include "hlslcc.h" + +static int InOutSupported(const ShaderLang eLang) +{ + if(eLang == LANG_ES_100 || eLang == LANG_120) + { + return 0; + } + return 1; +} + +static int WriteToFragData(const ShaderLang eLang) +{ + if(eLang == LANG_ES_100 || eLang == LANG_120) + { + return 1; + } + return 0; +} + +static int ShaderBitEncodingSupported(const ShaderLang eLang) +{ + if( eLang != LANG_ES_300 && + eLang != LANG_ES_310 && + eLang < LANG_330) + { + return 0; + } + return 1; +} + +static int HaveOverloadedTextureFuncs(const ShaderLang eLang) +{ + if(eLang == LANG_ES_100 || eLang == LANG_120) + { + return 0; + } + return 1; +} + +//Only enable for ES. +//Not present in 120, ignored in other desktop languages. +static int HavePrecisionQualifers(const ShaderLang eLang) +{ + if(eLang >= LANG_ES_100 && eLang <= LANG_ES_310) + { + return 1; + } + return 0; +} + +//Only on vertex inputs and pixel outputs. +static int HaveLimitedInOutLocationQualifier(const ShaderLang eLang, unsigned int flags) +{ + (void)flags; + + if(eLang >= LANG_330 || eLang == LANG_ES_300 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveInOutLocationQualifier(const ShaderLang eLang,const struct GlExtensions *extensions, unsigned int flags) +{ + (void)flags; + + if(eLang >= LANG_410 || eLang == LANG_ES_310 || (extensions && ((GlExtensions*)extensions)->ARB_explicit_attrib_location)) + { + return 1; + } + return 0; +} + +//layout(binding = X) uniform {uniformA; uniformB;} +//layout(location = X) uniform uniform_name; +static int HaveUniformBindingsAndLocations(const ShaderLang eLang,const struct GlExtensions *extensions, unsigned int flags) +{ + if (flags & HLSLCC_FLAG_DISABLE_EXPLICIT_LOCATIONS) + return 0; + + if (eLang >= LANG_430 || eLang == LANG_ES_310 || + (extensions && ((GlExtensions*)extensions)->ARB_explicit_uniform_location && ((GlExtensions*)extensions)->ARB_shading_language_420pack)) + { + return 1; + } + return 0; +} + +static int DualSourceBlendSupported(const ShaderLang eLang) +{ + if(eLang >= LANG_330) + { + return 1; + } + return 0; +} + +static int SubroutinesSupported(const ShaderLang eLang) +{ + if(eLang >= LANG_400) + { + return 1; + } + return 0; +} + +//Before 430, flat/smooth/centroid/noperspective must match +//between fragment and its previous stage. +//HLSL bytecode only tells us the interpolation in pixel shader. +static int PixelInterpDependency(const ShaderLang eLang) +{ + if(eLang < LANG_430) + { + return 1; + } + return 0; +} + +static int HaveUVec(const ShaderLang eLang) +{ + switch(eLang) + { + case LANG_ES_100: + case LANG_120: + return 0; + default: + break; + } + return 1; +} + +static int HaveGather(const ShaderLang eLang) +{ + if(eLang >= LANG_400 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveGatherNonConstOffset(const ShaderLang eLang) +{ + if(eLang >= LANG_420 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + + +static int HaveQueryLod(const ShaderLang eLang) +{ + if(eLang >= LANG_400) + { + return 1; + } + return 0; +} + +static int HaveQueryLevels(const ShaderLang eLang) +{ + if(eLang >= LANG_430) + { + return 1; + } + return 0; +} + + +static int HaveAtomicCounter(const ShaderLang eLang) +{ + if(eLang >= LANG_420 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveAtomicMem(const ShaderLang eLang) +{ + if(eLang >= LANG_430) + { + return 1; + } + return 0; +} + +static int HaveCompute(const ShaderLang eLang) +{ + if(eLang >= LANG_430 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +static int HaveImageLoadStore(const ShaderLang eLang) +{ + if(eLang >= LANG_420 || eLang == LANG_ES_310) + { + return 1; + } + return 0; +} + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/reflect.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/reflect.h new file mode 100644 index 0000000000..6db63de4ca --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/reflect.h @@ -0,0 +1,73 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef REFLECT_H +#define REFLECT_H + +#include "hlslcc.h" + +ResourceGroup ResourceTypeToResourceGroup(ResourceType); + +int GetResourceFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ShaderInfo* psShaderInfo, ResourceBinding** ppsOutBinding); + +void GetConstantBufferFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ShaderInfo* psShaderInfo, ConstantBuffer** ppsConstBuf); + +int GetInterfaceVarFromOffset(uint32_t ui32Offset, ShaderInfo* psShaderInfo, ShaderVar** ppsShaderVar); + +int GetInputSignatureFromRegister(const uint32_t ui32Register, const ShaderInfo* psShaderInfo, InOutSignature** ppsOut); +int GetOutputSignatureFromRegister(const uint32_t currentPhase, + const uint32_t ui32Register, + const uint32_t ui32Stream, + const uint32_t ui32CompMask, + ShaderInfo* psShaderInfo, + InOutSignature** ppsOut); + +int GetOutputSignatureFromSystemValue(SPECIAL_NAME eSystemValueType, uint32_t ui32SemanticIndex, ShaderInfo* psShaderInfo, InOutSignature** ppsOut); + +int GetShaderVarFromOffset(const uint32_t ui32Vec4Offset, + const uint32_t* pui32Swizzle, + ConstantBuffer* psCBuf, + ShaderVarType** ppsShaderVar, + int32_t* pi32Index, + int32_t* pi32Rebase); + +typedef struct +{ + uint32_t* pui32Inputs; + uint32_t* pui32Outputs; + uint32_t* pui32Resources; + uint32_t* pui32Interfaces; + uint32_t* pui32Inputs11; + uint32_t* pui32Outputs11; + uint32_t* pui32OutputsWithStreams; + uint32_t* pui32PatchConstants; + uint32_t* pui32Effects10Data; +} ReflectionChunks; + +void LoadShaderInfo(const uint32_t ui32MajorVersion, + const uint32_t ui32MinorVersion, + const ReflectionChunks* psChunks, + ShaderInfo* psInfo); + +void LoadD3D9ConstantTable(const char* data, + ShaderInfo* psInfo); + +void FreeShaderInfo(ShaderInfo* psShaderInfo); + +#if 0 +//--- Utility functions --- + +//Returns 0 if not found, 1 otherwise. +int GetResourceFromName(const char* name, ShaderInfo* psShaderInfo, ResourceBinding* psBinding); + +//These call into OpenGL and modify the uniforms of the currently bound program. +void SetResourceValueF(ResourceBinding* psBinding, float* value); +void SetResourceValueI(ResourceBinding* psBinding, int* value); +void SetResourceValueStr(ResourceBinding* psBinding, char* value); //Used for interfaces/subroutines. Also for constant buffers? + +void CreateUniformBufferObjectFromResource(ResourceBinding* psBinding, uint32_t* ui32GLHandle); +//------------------------ +#endif + +#endif + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/shaderLimits.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/shaderLimits.h new file mode 100644 index 0000000000..3561f7c78b --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/shaderLimits.h @@ -0,0 +1,14 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef HLSLCC_SHADER_LIMITS_H +#define HLSLCC_SHADER_LIMITS_H + +static enum {MAX_SHADER_VEC4_OUTPUT = 512}; +static enum {MAX_SHADER_VEC4_INPUT = 512}; +static enum {MAX_TEXTURES = 128}; +static enum {MAX_FUNCTION_BODIES = 1024}; +static enum {MAX_CLASS_TYPES = 1024}; +static enum {MAX_FUNCTION_POINTERS = 128}; + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structs.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structs.h new file mode 100644 index 0000000000..541b28d86b --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structs.h @@ -0,0 +1,338 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef STRUCTS_H +#define STRUCTS_H + +#include "hlslcc.h" +#include "bstrlib.h" + +#include "internal_includes/tokens.h" +#include "internal_includes/reflect.h" + +enum +{ + MAX_SUB_OPERANDS = 3 +}; + +typedef struct Operand_TAG +{ + int iExtended; + OPERAND_TYPE eType; + OPERAND_MODIFIER eModifier; + OPERAND_MIN_PRECISION eMinPrecision; + int iIndexDims; + int indexRepresentation[4]; + int writeMask; + int iGSInput; + int iWriteMaskEnabled; + + int iNumComponents; + + OPERAND_4_COMPONENT_SELECTION_MODE eSelMode; + uint32_t ui32CompMask; + uint32_t ui32Swizzle; + uint32_t aui32Swizzle[4]; + + uint32_t aui32ArraySizes[3]; + uint32_t ui32RegisterNumber; + //If eType is OPERAND_TYPE_IMMEDIATE32 + float afImmediates[4]; + //If eType is OPERAND_TYPE_IMMEDIATE64 + double adImmediates[4]; + + int iIntegerImmediate; + + SPECIAL_NAME eSpecialName; + char pszSpecialName[64]; + + OPERAND_INDEX_REPRESENTATION eIndexRep[3]; + + struct Operand_TAG* psSubOperand[MAX_SUB_OPERANDS]; + + //One type for each component. + SHADER_VARIABLE_TYPE aeDataType[4]; + +#ifdef _DEBUG + uint64_t id; +#endif +} Operand; + +typedef struct Instruction_TAG +{ + OPCODE_TYPE eOpcode; + INSTRUCTION_TEST_BOOLEAN eBooleanTestType; + COMPARISON_DX9 eDX9TestType; + uint32_t ui32SyncFlags; + uint32_t ui32NumOperands; + uint32_t ui32FirstSrc; + Operand asOperands[6]; + uint32_t bSaturate; + uint32_t ui32FuncIndexWithinInterface; + RESINFO_RETURN_TYPE eResInfoReturnType; + + int bAddressOffset; + int8_t iUAddrOffset; + int8_t iVAddrOffset; + int8_t iWAddrOffset; + RESOURCE_RETURN_TYPE xType, yType, zType, wType; + RESOURCE_DIMENSION eResDim; + +#ifdef _DEBUG + uint64_t id; +#endif +} Instruction; + +enum +{ + MAX_IMMEDIATE_CONST_BUFFER_VEC4_SIZE = 1024 +}; +enum +{ + MAX_TEXTURE_SAMPLERS_PAIRS = 32 +}; + +typedef struct ICBVec4_TAG +{ + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; +} ICBVec4; + +typedef struct Declaration_TAG +{ + OPCODE_TYPE eOpcode; + + uint32_t ui32NumOperands; + + Operand asOperands[2]; + + ICBVec4 asImmediateConstBuffer[MAX_IMMEDIATE_CONST_BUFFER_VEC4_SIZE]; + //The declaration can set one of these + //values depending on the opcode. + union + { + uint32_t ui32GlobalFlags; + uint32_t ui32NumTemps; + RESOURCE_DIMENSION eResourceDimension; + CONSTANT_BUFFER_ACCESS_PATTERN eCBAccessPattern; + INTERPOLATION_MODE eInterpolation; + PRIMITIVE_TOPOLOGY eOutputPrimitiveTopology; + PRIMITIVE eInputPrimitive; + uint32_t ui32MaxOutputVertexCount; + TESSELLATOR_DOMAIN eTessDomain; + TESSELLATOR_PARTITIONING eTessPartitioning; + TESSELLATOR_OUTPUT_PRIMITIVE eTessOutPrim; + uint32_t aui32WorkGroupSize[3]; + //Fork phase index followed by the instance count. + uint32_t aui32HullPhaseInstanceInfo[2]; + float fMaxTessFactor; + uint32_t ui32IndexRange; + uint32_t ui32GSInstanceCount; + + struct Interface_TAG + { + uint32_t ui32InterfaceID; + uint32_t ui32NumFuncTables; + uint32_t ui32ArraySize; + } interface; + } value; + + struct UAV_TAG + { + uint32_t ui32GloballyCoherentAccess; + uint32_t ui32BufferSize; + uint8_t bCounter; + RESOURCE_RETURN_TYPE Type; + } sUAV; + + struct TGSM_TAG + { + uint32_t ui32Stride; + uint32_t ui32Count; + } sTGSM; + + struct IndexableTemp_TAG + { + uint32_t ui32RegIndex; + uint32_t ui32RegCount; + uint32_t ui32RegComponentSize; + } sIdxTemp; + + uint32_t ui32TableLength; + + uint32_t ui32IsShadowTex; + + uint32_t ui32SamplerUsed[MAX_TEXTURE_SAMPLERS_PAIRS]; + uint32_t ui32SamplerUsedCount; + + uint32_t bIsComparisonSampler; +} Declaration; + +enum +{ + MAX_TEMP_VEC4 = 512 +}; + +enum +{ + MAX_GROUPSHARED = 8 +}; + +enum +{ + MAX_COLOR_MRT = 8 +}; + +enum +{ + MAX_DX9_IMMCONST = 256 +}; + +static const uint32_t MAIN_PHASE = 0; +static const uint32_t HS_GLOBAL_DECL = 1; +static const uint32_t HS_CTRL_POINT_PHASE = 2; +static const uint32_t HS_FORK_PHASE = 3; +static const uint32_t HS_JOIN_PHASE = 4; +enum +{ + NUM_PHASES = 5 +}; + +typedef struct ShaderPhase_TAG +{ + //How many instances of this phase type are there? + uint32_t ui32InstanceCount; + + uint32_t* pui32DeclCount; + Declaration** ppsDecl; + + uint32_t* pui32InstCount; + Instruction** ppsInst; +} ShaderPhase; + +typedef struct Shader_TAG +{ + uint32_t ui32MajorVersion; + uint32_t ui32MinorVersion; + SHADER_TYPE eShaderType; + + ShaderLang eTargetLanguage; + const struct GlExtensions* extensions; + + int fp64; + + //DWORDs in program code, including version and length tokens. + uint32_t ui32ShaderLength; + + //Instruction* functions;//non-main subroutines + + uint32_t aui32FuncTableToFuncPointer[MAX_FUNCTION_TABLES];//FIXME dynamic alloc + uint32_t aui32FuncBodyToFuncTable[MAX_FUNCTION_BODIES]; + + struct + { + uint32_t aui32FuncBodies[MAX_FUNCTION_BODIES]; + }funcTable[MAX_FUNCTION_TABLES]; + + struct + { + uint32_t aui32FuncTables[MAX_FUNCTION_TABLES]; + uint32_t ui32NumBodiesPerTable; + }funcPointer[MAX_FUNCTION_POINTERS]; + + uint32_t ui32NextClassFuncName[MAX_CLASS_TYPES]; + + const uint32_t* pui32FirstToken;//Reference for calculating current position in token stream. + + ShaderPhase asPhase[NUM_PHASES]; + + ShaderInfo sInfo; + + int abScalarInput[MAX_SHADER_VEC4_INPUT]; + + int aIndexedOutput[MAX_SHADER_VEC4_OUTPUT]; + + int aIndexedInput[MAX_SHADER_VEC4_INPUT]; + int aIndexedInputParents[MAX_SHADER_VEC4_INPUT]; + + RESOURCE_DIMENSION aeResourceDims[MAX_TEXTURES]; + + int aiInputDeclaredSize[MAX_SHADER_VEC4_INPUT]; + + int aiOutputDeclared[MAX_SHADER_VEC4_OUTPUT]; + + //Does not track built-in inputs. + int abInputReferencedByInstruction[MAX_SHADER_VEC4_INPUT]; + + int aiOpcodeUsed[NUM_OPCODES]; + + uint32_t ui32CurrentVertexOutputStream; + + uint32_t ui32NumDx9ImmConst; + uint32_t aui32Dx9ImmConstArrayRemap[MAX_DX9_IMMCONST]; + + ShaderVarType sGroupSharedVarType[MAX_GROUPSHARED]; + + TextureSamplerInfo textureSamplerInfo; +} ShaderData; + +// CONFETTI NOTE: DAVID SROUR +// The following is super sketchy, but at the moment, +// there is no way to figure out the type of a resource +// since HLSL has only register sets for the following: +// bool, int4, float4, sampler. +enum +{ + GMEM_FLOAT4_START_SLOT = 120 +}; +enum +{ + GMEM_FLOAT3_START_SLOT = 112 +}; +enum +{ + GMEM_FLOAT2_START_SLOT = 104 +}; +enum +{ + GMEM_FLOAT_START_SLOT = 96 +}; + +// CONFETTI NOTE +// Set the starting binding point for UAV_Buffer. +// All the binding points after the starting point is reserved for UAV +// only. This apply for both [[texture]] and [[buffer]] +enum +{ + UAV_BUFFER_START_SLOT = 25 +}; + +typedef struct HLSLCrossCompilerContext_TAG +{ + bstring mainShader; + bstring stagedInputDeclarations; // Metal only + bstring parameterDeclarations; // Metal only + bstring declaredOutputs; // Metal only + bstring earlyMain;//Code to be inserted at the start of main() + bstring postShaderCode[NUM_PHASES];//End of main or before emit() + + bstring* currentShaderString;//either mainShader or earlyMain + + int needsFragmentTestHint; // METAL only + + int havePostShaderCode[NUM_PHASES]; + uint32_t currentPhase; + + // GMEM INPUT AND OUTPUT TYPES MUST MATCH! + // THIS TABLE KEEPS TRACK OF WHAT THE OUTPUT TYPE SHOULD + // BE IF GMEM INPUT WAS DECLARED TO THE SAME SLOT # + uint32_t gmemOutputNumElements[MAX_COLOR_MRT]; // Metal only + + int indent; + unsigned int flags; + ShaderData* psShader; +} HLSLCrossCompilerContext; + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMETAL.c b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMETAL.c new file mode 100644 index 0000000000..d380100d83 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMETAL.c @@ -0,0 +1,15 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates + +#include "structsMetal.h" + +int IsAtomicVar(const ShaderVarType* const var, AtomicVarList* const list) +{ + for (uint32_t i = 0; i < list->Filled; i++) + { + if (var == list->AtomicVars[i]) + { + return 1; + } + } + return 0; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMetal.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMetal.h new file mode 100644 index 0000000000..cd63921310 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/structsMetal.h @@ -0,0 +1,19 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates + +#ifndef STRUCTSS_METAL_H +#define STRUCTSS_METAL_H + +#include "hlslcc.h" +#include + +typedef struct AtomicVarList_s +{ + const ShaderVarType** AtomicVars; + uint32_t Filled; + uint32_t Size; +} AtomicVarList; + +int IsAtomicVar(const ShaderVarType* const var, AtomicVarList* const list); + + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLDeclaration.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLDeclaration.h new file mode 100644 index 0000000000..d18ee2c243 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLDeclaration.h @@ -0,0 +1,19 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_GLSL_DECLARATION_H +#define TO_GLSL_DECLARATION_H + +#include "internal_includes/structs.h" + +void TranslateDeclaration(HLSLCrossCompilerContext* psContext, const Declaration* psDecl); + +const char* GetDeclaredInputName(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand); +const char* GetDeclaredOutputName(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand, int* stream); + +//Hull shaders have multiple phases. +//Each phase has its own temps. +//Convert to global temps for GLSL. +void ConsolidateHullTempVars(ShaderData* psShader); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLInstruction.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLInstruction.h new file mode 100644 index 0000000000..34f67cfe46 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLInstruction.h @@ -0,0 +1,18 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_GLSL_INSTRUCTION_H +#define TO_GLSL_INSTRUCTION_H + +#include "internal_includes/structs.h" + +void TranslateInstruction(HLSLCrossCompilerContext* psContext, Instruction* psInst, Instruction* psNextInst); + +//For each MOV temp, immediate; check to see if the next instruction +//using that temp has an integer opcode. If so then the immediate value +//is flaged as having an integer encoding. +void MarkIntegerImmediates(HLSLCrossCompilerContext* psContext); + +void SetDataTypes(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLOperand.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLOperand.h new file mode 100644 index 0000000000..1d7430504c --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toGLSLOperand.h @@ -0,0 +1,72 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_GLSL_OPERAND_H +#define TO_GLSL_OPERAND_H + +#include "internal_includes/structs.h" + +#define TO_FLAG_NONE 0x0 +#define TO_FLAG_INTEGER 0x1 +#define TO_FLAG_NAME_ONLY 0x2 +#define TO_FLAG_DECLARATION_NAME 0x4 +#define TO_FLAG_DESTINATION 0x8 //Operand is being written to by assignment. +#define TO_FLAG_UNSIGNED_INTEGER 0x10 +#define TO_FLAG_DOUBLE 0x20 +#define TO_FLAG_FLOAT16 0x40 +// --- TO_AUTO_BITCAST_TO_FLOAT --- +//If the operand is an integer temp variable then this flag +//indicates that the temp has a valid floating point encoding +//and that the current expression expects the operand to be floating point +//and therefore intBitsToFloat must be applied to that variable. +#define TO_AUTO_BITCAST_TO_FLOAT 0x80 +#define TO_AUTO_BITCAST_TO_INT 0x100 +#define TO_AUTO_BITCAST_TO_UINT 0x200 +#define TO_AUTO_BITCAST_TO_FLOAT16 0x400 +// AUTO_EXPAND flags automatically expand the operand to at least (i/u)vecX +// to match HLSL functionality. +#define TO_AUTO_EXPAND_TO_VEC2 0x800 +#define TO_AUTO_EXPAND_TO_VEC3 0x1000 +#define TO_AUTO_EXPAND_TO_VEC4 0x2000 + + +void TranslateOperand(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag); +// Translate operand but add additional component mask +void TranslateOperandWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t ui32ComponentMask); + +int GetMaxComponentFromComponentMask(const Operand* psOperand); +void TranslateOperandIndex(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index); +void TranslateOperandIndexMAD(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add); +void TranslateOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +void TranslateOperandSwizzleWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask); + +uint32_t GetNumSwizzleElements(const Operand* psOperand); +uint32_t GetNumSwizzleElementsWithMask(const Operand *psOperand, uint32_t ui32CompMask); +void AddSwizzleUsingElementCount(HLSLCrossCompilerContext* psContext, uint32_t count); +int GetFirstOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +uint32_t IsSwizzleReplicated(const Operand* psOperand); + +void ResourceName(bstring targetStr, HLSLCrossCompilerContext* psContext, ResourceGroup group, const uint32_t ui32RegisterNumber, const int bZCompare); + +bstring TextureSamplerName(ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare); +void ConcatTextureSamplerName(bstring str, ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare); + +//Non-zero means the components overlap +int CompareOperandSwizzles(const Operand* psOperandA, const Operand* psOperandB); + +// Returns the write mask for the operand used for destination +uint32_t GetOperandWriteMask(const Operand *psOperand); + +SHADER_VARIABLE_TYPE GetOperandDataType(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +SHADER_VARIABLE_TYPE GetOperandDataTypeEx(HLSLCrossCompilerContext* psContext, const Operand* psOperand, SHADER_VARIABLE_TYPE ePreferredTypeForImmediates); + +const char * GetConstructorForType(const SHADER_VARIABLE_TYPE eType, + const int components); + +const char * GetConstructorForTypeFlag(const uint32_t ui32Flag, + const int components); + +uint32_t SVTTypeToFlag(const SHADER_VARIABLE_TYPE eType); +SHADER_VARIABLE_TYPE TypeFlagsToSVTType(const uint32_t typeflags); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALDeclaration.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALDeclaration.h new file mode 100644 index 0000000000..fb10d5b691 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALDeclaration.h @@ -0,0 +1,15 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_METAL_DECLARATION_H +#define TO_METAL_DECLARATION_H + +#include "internal_includes/structs.h" +#include "internal_includes/structsMetal.h" + +void TranslateDeclarationMETAL(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, AtomicVarList* psAtomicList); + +char* GetDeclaredInputNameMETAL(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand); +char* GetDeclaredOutputNameMETAL(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALInstruction.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALInstruction.h new file mode 100644 index 0000000000..e5b267cd58 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALInstruction.h @@ -0,0 +1,20 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_METAL_INSTRUCTION_H +#define TO_METAL_INSTRUCTION_H + +#include "internal_includes/structs.h" +#include "structsMetal.h" + +void TranslateInstructionMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, Instruction* psNextInst); +void DetectAtomicInstructionMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, Instruction* psNextInst, AtomicVarList* psAtomicList); + +//For each MOV temp, immediate; check to see if the next instruction +//using that temp has an integer opcode. If so then the immediate value +//is flaged as having an integer encoding. +void MarkIntegerImmediatesMETAL(HLSLCrossCompilerContext* psContext); + +void SetDataTypesMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALOperand.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALOperand.h new file mode 100644 index 0000000000..6cebf5bab6 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/toMETALOperand.h @@ -0,0 +1,78 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TO_METAL_OPERAND_H +#define TO_METAL_OPERAND_H + +#include "internal_includes/structs.h" + +#define TO_FLAG_NONE 0x0 +#define TO_FLAG_INTEGER 0x1 +#define TO_FLAG_NAME_ONLY 0x2 +#define TO_FLAG_DECLARATION_NAME 0x4 +#define TO_FLAG_DESTINATION 0x8 //Operand is being written to by assignment. +#define TO_FLAG_UNSIGNED_INTEGER 0x10 +#define TO_FLAG_DOUBLE 0x20 +#define TO_FLAG_FLOAT16 0x40 +// --- TO_AUTO_BITCAST_TO_FLOAT --- +//If the operand is an integer temp variable then this flag +//indicates that the temp has a valid floating point encoding +//and that the current expression expects the operand to be floating point +//and therefore intBitsToFloat must be applied to that variable. +#define TO_AUTO_BITCAST_TO_FLOAT 0x80 +#define TO_AUTO_BITCAST_TO_INT 0x100 +#define TO_AUTO_BITCAST_TO_UINT 0x200 +#define TO_AUTO_BITCAST_TO_FLOAT16 0x400 +// AUTO_EXPAND flags automatically expand the operand to at least (i/u)vecX +// to match HLSL functionality. +#define TO_AUTO_EXPAND_TO_VEC2 0x800 +#define TO_AUTO_EXPAND_TO_VEC3 0x1000 +#define TO_AUTO_EXPAND_TO_VEC4 0x2000 + +void TranslateOperandMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag); +// Translate operand but add additional component mask +void TranslateOperandWithMaskMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t ui32ComponentMask); + +int GetMaxComponentFromComponentMaskMETAL(const Operand* psOperand); +void TranslateOperandIndexMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index); +void TranslateOperandIndexMADMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add); +void TranslateOperandSwizzleMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +void TranslateOperandSwizzleWithMaskMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask); + +void TranslateGmemOperandSwizzleWithMaskMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask, uint32_t gmemNumElements); + +uint32_t GetNumSwizzleElementsMETAL(const Operand* psOperand); +uint32_t GetNumSwizzleElementsWithMaskMETAL(const Operand *psOperand, uint32_t ui32CompMask); +void AddSwizzleUsingElementCountMETAL(HLSLCrossCompilerContext* psContext, uint32_t count); +int GetFirstOperandSwizzleMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +uint32_t IsSwizzleReplicatedMETAL(const Operand* psOperand); + +void ResourceNameMETAL(bstring targetStr, HLSLCrossCompilerContext* psContext, ResourceGroup group, const uint32_t ui32RegisterNumber, const int bZCompare); + +bstring TextureSamplerNameMETAL(ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare); +void ConcatTextureSamplerNameMETAL(bstring str, ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare); + +//Non-zero means the components overlap +int CompareOperandSwizzlesMETAL(const Operand* psOperandA, const Operand* psOperandB); + +// Returns the write mask for the operand used for destination +uint32_t GetOperandWriteMaskMETAL(const Operand *psOperand); + +SHADER_VARIABLE_TYPE GetOperandDataTypeMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand); +SHADER_VARIABLE_TYPE GetOperandDataTypeExMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, SHADER_VARIABLE_TYPE ePreferredTypeForImmediates); + +const char * GetConstructorForTypeMETAL(const SHADER_VARIABLE_TYPE eType, + const int components); + +const char * GetConstructorForTypeFlagMETAL(const uint32_t ui32Flag, + const int components); + +uint32_t SVTTypeToFlagMETAL(const SHADER_VARIABLE_TYPE eType); +SHADER_VARIABLE_TYPE TypeFlagsToSVTTypeMETAL(const uint32_t typeflags); + + +uint32_t GetGmemInputResourceSlotMETAL(uint32_t const slotIn); + +uint32_t GetGmemInputResourceNumElementsMETAL(uint32_t const slotIn); + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokens.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokens.h new file mode 100644 index 0000000000..ddf17058cd --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokens.h @@ -0,0 +1,819 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#ifndef TOKENS_H +#define TOKENS_H + +#include "hlslcc.h" + +typedef enum +{ + INVALID_SHADER = -1, + PIXEL_SHADER, + VERTEX_SHADER, + GEOMETRY_SHADER, + HULL_SHADER, + DOMAIN_SHADER, + COMPUTE_SHADER, +} SHADER_TYPE; + +static SHADER_TYPE DecodeShaderType(uint32_t ui32Token) +{ + return (SHADER_TYPE)((ui32Token & 0xffff0000) >> 16); +} + +static uint32_t DecodeProgramMajorVersion(uint32_t ui32Token) +{ + return (ui32Token & 0x000000f0) >> 4; +} + +static uint32_t DecodeProgramMinorVersion(uint32_t ui32Token) +{ + return (ui32Token & 0x0000000f); +} + +static uint32_t DecodeInstructionLength(uint32_t ui32Token) +{ + return (ui32Token & 0x7f000000) >> 24; +} + +static uint32_t DecodeIsOpcodeExtended(uint32_t ui32Token) +{ + return (ui32Token & 0x80000000) >> 31; +} + +typedef enum EXTENDED_OPCODE_TYPE +{ + EXTENDED_OPCODE_EMPTY = 0, + EXTENDED_OPCODE_SAMPLE_CONTROLS = 1, + EXTENDED_OPCODE_RESOURCE_DIM = 2, + EXTENDED_OPCODE_RESOURCE_RETURN_TYPE = 3, +} EXTENDED_OPCODE_TYPE; + +static EXTENDED_OPCODE_TYPE DecodeExtendedOpcodeType(uint32_t ui32Token) +{ + return (EXTENDED_OPCODE_TYPE)(ui32Token & 0x0000003f); +} + +typedef enum RESOURCE_RETURN_TYPE +{ + RETURN_TYPE_UNORM = 1, + RETURN_TYPE_SNORM = 2, + RETURN_TYPE_SINT = 3, + RETURN_TYPE_UINT = 4, + RETURN_TYPE_FLOAT = 5, + RETURN_TYPE_MIXED = 6, + RETURN_TYPE_DOUBLE = 7, + RETURN_TYPE_CONTINUED = 8, + RETURN_TYPE_UNUSED = 9, +} RESOURCE_RETURN_TYPE; + +static RESOURCE_RETURN_TYPE DecodeResourceReturnType(uint32_t ui32Coord, uint32_t ui32Token) +{ + return (RESOURCE_RETURN_TYPE)((ui32Token>>(ui32Coord * 4))&0xF); +} + +static RESOURCE_RETURN_TYPE DecodeExtendedResourceReturnType(uint32_t ui32Coord, uint32_t ui32Token) +{ + return (RESOURCE_RETURN_TYPE)((ui32Token>>(ui32Coord * 4 + 6))&0xF); +} + +typedef enum +{ + //For DX9 + OPCODE_POW = -6, + OPCODE_DP2ADD = -5, + OPCODE_LRP = -4, + OPCODE_ENDREP = -3, + OPCODE_REP = -2, + OPCODE_SPECIAL_DCL_IMMCONST = -1, + + OPCODE_ADD, + OPCODE_AND, + OPCODE_BREAK, + OPCODE_BREAKC, + OPCODE_CALL, + OPCODE_CALLC, + OPCODE_CASE, + OPCODE_CONTINUE, + OPCODE_CONTINUEC, + OPCODE_CUT, + OPCODE_DEFAULT, + OPCODE_DERIV_RTX, + OPCODE_DERIV_RTY, + OPCODE_DISCARD, + OPCODE_DIV, + OPCODE_DP2, + OPCODE_DP3, + OPCODE_DP4, + OPCODE_ELSE, + OPCODE_EMIT, + OPCODE_EMITTHENCUT, + OPCODE_ENDIF, + OPCODE_ENDLOOP, + OPCODE_ENDSWITCH, + OPCODE_EQ, + OPCODE_EXP, + OPCODE_FRC, + OPCODE_FTOI, + OPCODE_FTOU, + OPCODE_GE, + OPCODE_IADD, + OPCODE_IF, + OPCODE_IEQ, + OPCODE_IGE, + OPCODE_ILT, + OPCODE_IMAD, + OPCODE_IMAX, + OPCODE_IMIN, + OPCODE_IMUL, + OPCODE_INE, + OPCODE_INEG, + OPCODE_ISHL, + OPCODE_ISHR, + OPCODE_ITOF, + OPCODE_LABEL, + OPCODE_LD, + OPCODE_LD_MS, + OPCODE_LOG, + OPCODE_LOOP, + OPCODE_LT, + OPCODE_MAD, + OPCODE_MIN, + OPCODE_MAX, + OPCODE_CUSTOMDATA, + OPCODE_MOV, + OPCODE_MOVC, + OPCODE_MUL, + OPCODE_NE, + OPCODE_NOP, + OPCODE_NOT, + OPCODE_OR, + OPCODE_RESINFO, + OPCODE_RET, + OPCODE_RETC, + OPCODE_ROUND_NE, + OPCODE_ROUND_NI, + OPCODE_ROUND_PI, + OPCODE_ROUND_Z, + OPCODE_RSQ, + OPCODE_SAMPLE, + OPCODE_SAMPLE_C, + OPCODE_SAMPLE_C_LZ, + OPCODE_SAMPLE_L, + OPCODE_SAMPLE_D, + OPCODE_SAMPLE_B, + OPCODE_SQRT, + OPCODE_SWITCH, + OPCODE_SINCOS, + OPCODE_UDIV, + OPCODE_ULT, + OPCODE_UGE, + OPCODE_UMUL, + OPCODE_UMAD, + OPCODE_UMAX, + OPCODE_UMIN, + OPCODE_USHR, + OPCODE_UTOF, + OPCODE_XOR, + OPCODE_DCL_RESOURCE, // DCL* opcodes have + OPCODE_DCL_CONSTANT_BUFFER, // custom operand formats. + OPCODE_DCL_SAMPLER, + OPCODE_DCL_INDEX_RANGE, + OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY, + OPCODE_DCL_GS_INPUT_PRIMITIVE, + OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT, + OPCODE_DCL_INPUT, + OPCODE_DCL_INPUT_SGV, + OPCODE_DCL_INPUT_SIV, + OPCODE_DCL_INPUT_PS, + OPCODE_DCL_INPUT_PS_SGV, + OPCODE_DCL_INPUT_PS_SIV, + OPCODE_DCL_OUTPUT, + OPCODE_DCL_OUTPUT_SGV, + OPCODE_DCL_OUTPUT_SIV, + OPCODE_DCL_TEMPS, + OPCODE_DCL_INDEXABLE_TEMP, + OPCODE_DCL_GLOBAL_FLAGS, + +// ----------------------------------------------- + + OPCODE_RESERVED_10, + +// ---------- DX 10.1 op codes--------------------- + + OPCODE_LOD, + OPCODE_GATHER4, + OPCODE_SAMPLE_POS, + OPCODE_SAMPLE_INFO, + +// ----------------------------------------------- + + // This should be 10.1's version of NUM_OPCODES + OPCODE_RESERVED_10_1, + +// ---------- DX 11 op codes--------------------- + OPCODE_HS_DECLS, // token marks beginning of HS sub-shader + OPCODE_HS_CONTROL_POINT_PHASE, // token marks beginning of HS sub-shader + OPCODE_HS_FORK_PHASE, // token marks beginning of HS sub-shader + OPCODE_HS_JOIN_PHASE, // token marks beginning of HS sub-shader + + OPCODE_EMIT_STREAM, + OPCODE_CUT_STREAM, + OPCODE_EMITTHENCUT_STREAM, + OPCODE_INTERFACE_CALL, + + OPCODE_BUFINFO, + OPCODE_DERIV_RTX_COARSE, + OPCODE_DERIV_RTX_FINE, + OPCODE_DERIV_RTY_COARSE, + OPCODE_DERIV_RTY_FINE, + OPCODE_GATHER4_C, + OPCODE_GATHER4_PO, + OPCODE_GATHER4_PO_C, + OPCODE_RCP, + OPCODE_F32TOF16, + OPCODE_F16TOF32, + OPCODE_UADDC, + OPCODE_USUBB, + OPCODE_COUNTBITS, + OPCODE_FIRSTBIT_HI, + OPCODE_FIRSTBIT_LO, + OPCODE_FIRSTBIT_SHI, + OPCODE_UBFE, + OPCODE_IBFE, + OPCODE_BFI, + OPCODE_BFREV, + OPCODE_SWAPC, + + OPCODE_DCL_STREAM, + OPCODE_DCL_FUNCTION_BODY, + OPCODE_DCL_FUNCTION_TABLE, + OPCODE_DCL_INTERFACE, + + OPCODE_DCL_INPUT_CONTROL_POINT_COUNT, + OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT, + OPCODE_DCL_TESS_DOMAIN, + OPCODE_DCL_TESS_PARTITIONING, + OPCODE_DCL_TESS_OUTPUT_PRIMITIVE, + OPCODE_DCL_HS_MAX_TESSFACTOR, + OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT, + OPCODE_DCL_HS_JOIN_PHASE_INSTANCE_COUNT, + + OPCODE_DCL_THREAD_GROUP, + OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED, + OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW, + OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED, + OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW, + OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED, + OPCODE_DCL_RESOURCE_RAW, + OPCODE_DCL_RESOURCE_STRUCTURED, + OPCODE_LD_UAV_TYPED, + OPCODE_STORE_UAV_TYPED, + OPCODE_LD_RAW, + OPCODE_STORE_RAW, + OPCODE_LD_STRUCTURED, + OPCODE_STORE_STRUCTURED, + OPCODE_ATOMIC_AND, + OPCODE_ATOMIC_OR, + OPCODE_ATOMIC_XOR, + OPCODE_ATOMIC_CMP_STORE, + OPCODE_ATOMIC_IADD, + OPCODE_ATOMIC_IMAX, + OPCODE_ATOMIC_IMIN, + OPCODE_ATOMIC_UMAX, + OPCODE_ATOMIC_UMIN, + OPCODE_IMM_ATOMIC_ALLOC, + OPCODE_IMM_ATOMIC_CONSUME, + OPCODE_IMM_ATOMIC_IADD, + OPCODE_IMM_ATOMIC_AND, + OPCODE_IMM_ATOMIC_OR, + OPCODE_IMM_ATOMIC_XOR, + OPCODE_IMM_ATOMIC_EXCH, + OPCODE_IMM_ATOMIC_CMP_EXCH, + OPCODE_IMM_ATOMIC_IMAX, + OPCODE_IMM_ATOMIC_IMIN, + OPCODE_IMM_ATOMIC_UMAX, + OPCODE_IMM_ATOMIC_UMIN, + OPCODE_SYNC, + + OPCODE_DADD, + OPCODE_DMAX, + OPCODE_DMIN, + OPCODE_DMUL, + OPCODE_DEQ, + OPCODE_DGE, + OPCODE_DLT, + OPCODE_DNE, + OPCODE_DMOV, + OPCODE_DMOVC, + OPCODE_DTOF, + OPCODE_FTOD, + + OPCODE_EVAL_SNAPPED, + OPCODE_EVAL_SAMPLE_INDEX, + OPCODE_EVAL_CENTROID, + + OPCODE_DCL_GS_INSTANCE_COUNT, + + OPCODE_ABORT, + OPCODE_DEBUG_BREAK, + +// ----------------------------------------------- + + // This marks the end of D3D11.0 opcodes + OPCODE_RESERVED_11, + + OPCODE_DDIV, + OPCODE_DFMA, + OPCODE_DRCP, + + OPCODE_MSAD, + + OPCODE_DTOI, + OPCODE_DTOU, + OPCODE_ITOD, + OPCODE_UTOD, + +// ----------------------------------------------- + + // This marks the end of D3D11.1 opcodes + OPCODE_RESERVED_11_1, + + NUM_OPCODES, + OPCODE_INVAILD = NUM_OPCODES, +} OPCODE_TYPE; + +static OPCODE_TYPE DecodeOpcodeType(uint32_t ui32Token) +{ + return (OPCODE_TYPE)(ui32Token & 0x00007ff); +} + +typedef enum +{ + INDEX_0D, + INDEX_1D, + INDEX_2D, + INDEX_3D, +} OPERAND_INDEX_DIMENSION; + +static OPERAND_INDEX_DIMENSION DecodeOperandIndexDimension(uint32_t ui32Token) +{ + return (OPERAND_INDEX_DIMENSION)((ui32Token & 0x00300000) >> 20); +} + +typedef enum OPERAND_TYPE +{ + OPERAND_TYPE_SPECIAL_LOOPCOUNTER = -10, + OPERAND_TYPE_SPECIAL_IMMCONSTINT = -9, + OPERAND_TYPE_SPECIAL_TEXCOORD = -8, + OPERAND_TYPE_SPECIAL_POSITION = -7, + OPERAND_TYPE_SPECIAL_FOG = -6, + OPERAND_TYPE_SPECIAL_POINTSIZE = -5, + OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR = -4, + OPERAND_TYPE_SPECIAL_OUTBASECOLOUR = -3, + OPERAND_TYPE_SPECIAL_ADDRESS = -2, + OPERAND_TYPE_SPECIAL_IMMCONST = -1, + OPERAND_TYPE_TEMP = 0, // Temporary Register File + OPERAND_TYPE_INPUT = 1, // General Input Register File + OPERAND_TYPE_OUTPUT = 2, // General Output Register File + OPERAND_TYPE_INDEXABLE_TEMP = 3, // Temporary Register File (indexable) + OPERAND_TYPE_IMMEDIATE32 = 4, // 32bit/component immediate value(s) + // If for example, operand token bits + // [01:00]==OPERAND_4_COMPONENT, + // this means that the operand type: + // OPERAND_TYPE_IMMEDIATE32 + // results in 4 additional 32bit + // DWORDS present for the operand. + OPERAND_TYPE_IMMEDIATE64 = 5, // 64bit/comp.imm.val(s)HI:LO + OPERAND_TYPE_SAMPLER = 6, // Reference to sampler state + OPERAND_TYPE_RESOURCE = 7, // Reference to memory resource (e.g. texture) + OPERAND_TYPE_CONSTANT_BUFFER= 8, // Reference to constant buffer + OPERAND_TYPE_IMMEDIATE_CONSTANT_BUFFER= 9, // Reference to immediate constant buffer + OPERAND_TYPE_LABEL = 10, // Label + OPERAND_TYPE_INPUT_PRIMITIVEID = 11, // Input primitive ID + OPERAND_TYPE_OUTPUT_DEPTH = 12, // Output Depth + OPERAND_TYPE_NULL = 13, // Null register, used to discard results of operations + // Below Are operands new in DX 10.1 + OPERAND_TYPE_RASTERIZER = 14, // DX10.1 Rasterizer register, used to denote the depth/stencil and render target resources + OPERAND_TYPE_OUTPUT_COVERAGE_MASK = 15, // DX10.1 PS output MSAA coverage mask (scalar) + // Below Are operands new in DX 11 + OPERAND_TYPE_STREAM = 16, // Reference to GS stream output resource + OPERAND_TYPE_FUNCTION_BODY = 17, // Reference to a function definition + OPERAND_TYPE_FUNCTION_TABLE = 18, // Reference to a set of functions used by a class + OPERAND_TYPE_INTERFACE = 19, // Reference to an interface + OPERAND_TYPE_FUNCTION_INPUT = 20, // Reference to an input parameter to a function + OPERAND_TYPE_FUNCTION_OUTPUT = 21, // Reference to an output parameter to a function + OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID = 22, // HS Control Point phase input saying which output control point ID this is + OPERAND_TYPE_INPUT_FORK_INSTANCE_ID = 23, // HS Fork Phase input instance ID + OPERAND_TYPE_INPUT_JOIN_INSTANCE_ID = 24, // HS Join Phase input instance ID + OPERAND_TYPE_INPUT_CONTROL_POINT = 25, // HS Fork+Join, DS phase input control points (array of them) + OPERAND_TYPE_OUTPUT_CONTROL_POINT = 26, // HS Fork+Join phase output control points (array of them) + OPERAND_TYPE_INPUT_PATCH_CONSTANT = 27, // DS+HSJoin Input Patch Constants (array of them) + OPERAND_TYPE_INPUT_DOMAIN_POINT = 28, // DS Input Domain point + OPERAND_TYPE_THIS_POINTER = 29, // Reference to an interface this pointer + OPERAND_TYPE_UNORDERED_ACCESS_VIEW = 30, // Reference to UAV u# + OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY = 31, // Reference to Thread Group Shared Memory g# + OPERAND_TYPE_INPUT_THREAD_ID = 32, // Compute Shader Thread ID + OPERAND_TYPE_INPUT_THREAD_GROUP_ID = 33, // Compute Shader Thread Group ID + OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP = 34, // Compute Shader Thread ID In Thread Group + OPERAND_TYPE_INPUT_COVERAGE_MASK = 35, // Pixel shader coverage mask input + OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED = 36, // Compute Shader Thread ID In Group Flattened to a 1D value. + OPERAND_TYPE_INPUT_GS_INSTANCE_ID = 37, // Input GS instance ID + OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL = 38, // Output Depth, forced to be greater than or equal than current depth + OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL = 39, // Output Depth, forced to be less than or equal to current depth + OPERAND_TYPE_CYCLE_COUNTER = 40, // Cycle counter +} OPERAND_TYPE; + +static OPERAND_TYPE DecodeOperandType(uint32_t ui32Token) +{ + return (OPERAND_TYPE)((ui32Token & 0x000ff000) >> 12); +} + +static SPECIAL_NAME DecodeOperandSpecialName(uint32_t ui32Token) +{ + return (SPECIAL_NAME)(ui32Token & 0x0000ffff); +} + +typedef enum OPERAND_INDEX_REPRESENTATION +{ + OPERAND_INDEX_IMMEDIATE32 = 0, // Extra DWORD + OPERAND_INDEX_IMMEDIATE64 = 1, // 2 Extra DWORDs + // (HI32:LO32) + OPERAND_INDEX_RELATIVE = 2, // Extra operand + OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE = 3, // Extra DWORD followed by + // extra operand + OPERAND_INDEX_IMMEDIATE64_PLUS_RELATIVE = 4, // 2 Extra DWORDS + // (HI32:LO32) followed + // by extra operand +} OPERAND_INDEX_REPRESENTATION; + +static OPERAND_INDEX_REPRESENTATION DecodeOperandIndexRepresentation(uint32_t ui32Dimension, uint32_t ui32Token) +{ + return (OPERAND_INDEX_REPRESENTATION)((ui32Token & (0x3<<(22+3*((ui32Dimension)&3)))) >> (22+3*((ui32Dimension)&3))); +} + +typedef enum OPERAND_NUM_COMPONENTS +{ + OPERAND_0_COMPONENT = 0, + OPERAND_1_COMPONENT = 1, + OPERAND_4_COMPONENT = 2, + OPERAND_N_COMPONENT = 3 // unused for now +} OPERAND_NUM_COMPONENTS; + +static OPERAND_NUM_COMPONENTS DecodeOperandNumComponents(uint32_t ui32Token) +{ + return (OPERAND_NUM_COMPONENTS)(ui32Token & 0x00000003); +} + +typedef enum OPERAND_4_COMPONENT_SELECTION_MODE +{ + OPERAND_4_COMPONENT_MASK_MODE = 0, // mask 4 components + OPERAND_4_COMPONENT_SWIZZLE_MODE = 1, // swizzle 4 components + OPERAND_4_COMPONENT_SELECT_1_MODE = 2, // select 1 of 4 components +} OPERAND_4_COMPONENT_SELECTION_MODE; + +static OPERAND_4_COMPONENT_SELECTION_MODE DecodeOperand4CompSelMode(uint32_t ui32Token) +{ + return (OPERAND_4_COMPONENT_SELECTION_MODE)((ui32Token & 0x0000000c) >> 2); +} + +#define OPERAND_4_COMPONENT_MASK_X 0x00000001 +#define OPERAND_4_COMPONENT_MASK_Y 0x00000002 +#define OPERAND_4_COMPONENT_MASK_Z 0x00000004 +#define OPERAND_4_COMPONENT_MASK_W 0x00000008 +#define OPERAND_4_COMPONENT_MASK_R OPERAND_4_COMPONENT_MASK_X +#define OPERAND_4_COMPONENT_MASK_G OPERAND_4_COMPONENT_MASK_Y +#define OPERAND_4_COMPONENT_MASK_B OPERAND_4_COMPONENT_MASK_Z +#define OPERAND_4_COMPONENT_MASK_A OPERAND_4_COMPONENT_MASK_W +#define OPERAND_4_COMPONENT_MASK_ALL 0x0000000f + +static uint32_t DecodeOperand4CompMask(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x000000f0) >> 4); +} + +static uint32_t DecodeOperand4CompSwizzle(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x00000ff0) >> 4); +} + +static uint32_t DecodeOperand4CompSel1(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x00000030) >> 4); +} + +#define OPERAND_4_COMPONENT_X 0 +#define OPERAND_4_COMPONENT_Y 1 +#define OPERAND_4_COMPONENT_Z 2 +#define OPERAND_4_COMPONENT_W 3 + +static uint32_t NO_SWIZZLE = (( (OPERAND_4_COMPONENT_X) | (OPERAND_4_COMPONENT_Y<<2) | (OPERAND_4_COMPONENT_Z << 4) | (OPERAND_4_COMPONENT_W << 6))/*<<4*/); + +static uint32_t XXXX_SWIZZLE = (((OPERAND_4_COMPONENT_X) | (OPERAND_4_COMPONENT_X<<2) | (OPERAND_4_COMPONENT_X << 4) | (OPERAND_4_COMPONENT_X << 6))); +static uint32_t YYYY_SWIZZLE = (((OPERAND_4_COMPONENT_Y) | (OPERAND_4_COMPONENT_Y<<2) | (OPERAND_4_COMPONENT_Y << 4) | (OPERAND_4_COMPONENT_Y << 6))); +static uint32_t ZZZZ_SWIZZLE = (((OPERAND_4_COMPONENT_Z) | (OPERAND_4_COMPONENT_Z<<2) | (OPERAND_4_COMPONENT_Z << 4) | (OPERAND_4_COMPONENT_Z << 6))); +static uint32_t WWWW_SWIZZLE = (((OPERAND_4_COMPONENT_W) | (OPERAND_4_COMPONENT_W<<2) | (OPERAND_4_COMPONENT_W << 4) | (OPERAND_4_COMPONENT_W << 6))); + +static uint32_t DecodeOperand4CompSwizzleSource(uint32_t ui32Token, uint32_t comp) +{ + return (uint32_t)(((ui32Token)>>(4+2*((comp)&3)))&3); +} + +typedef enum RESOURCE_DIMENSION +{ + RESOURCE_DIMENSION_UNKNOWN = 0, + RESOURCE_DIMENSION_BUFFER = 1, + RESOURCE_DIMENSION_TEXTURE1D = 2, + RESOURCE_DIMENSION_TEXTURE2D = 3, + RESOURCE_DIMENSION_TEXTURE2DMS = 4, + RESOURCE_DIMENSION_TEXTURE3D = 5, + RESOURCE_DIMENSION_TEXTURECUBE = 6, + RESOURCE_DIMENSION_TEXTURE1DARRAY = 7, + RESOURCE_DIMENSION_TEXTURE2DARRAY = 8, + RESOURCE_DIMENSION_TEXTURE2DMSARRAY = 9, + RESOURCE_DIMENSION_TEXTURECUBEARRAY = 10, + RESOURCE_DIMENSION_RAW_BUFFER = 11, + RESOURCE_DIMENSION_STRUCTURED_BUFFER = 12, +} RESOURCE_DIMENSION; + +static RESOURCE_DIMENSION DecodeResourceDimension(uint32_t ui32Token) +{ + return (RESOURCE_DIMENSION)((ui32Token & 0x0000f800) >> 11); +} + +static RESOURCE_DIMENSION DecodeExtendedResourceDimension(uint32_t ui32Token) +{ + return (RESOURCE_DIMENSION)((ui32Token & 0x000007C0) >> 6); +} + +static const uint32_t SHADER_INPUT_FLAG_COMPARISON_SAMPLER = (1 << 1); + +static uint32_t DecodeShaderInputFlags(uint32_t ui32Token) +{ + return (uint32_t)(ui32Token & 0x00000002); +} + +typedef enum CONSTANT_BUFFER_ACCESS_PATTERN +{ + CONSTANT_BUFFER_ACCESS_PATTERN_IMMEDIATEINDEXED = 0, + CONSTANT_BUFFER_ACCESS_PATTERN_DYNAMICINDEXED = 1 +} CONSTANT_BUFFER_ACCESS_PATTERN; + +static CONSTANT_BUFFER_ACCESS_PATTERN DecodeConstantBufferAccessPattern(uint32_t ui32Token) +{ + return (CONSTANT_BUFFER_ACCESS_PATTERN)((ui32Token & 0x00000800) >> 11); +} + +typedef enum INSTRUCTION_TEST_BOOLEAN +{ + INSTRUCTION_TEST_ZERO = 0, + INSTRUCTION_TEST_NONZERO = 1 +} INSTRUCTION_TEST_BOOLEAN; + +static INSTRUCTION_TEST_BOOLEAN DecodeInstrTestBool(uint32_t ui32Token) +{ + return (INSTRUCTION_TEST_BOOLEAN)((ui32Token & 0x00040000) >> 18); +} + +static uint32_t DecodeIsOperandExtended(uint32_t ui32Token) +{ + return (ui32Token & 0x80000000) >> 31; +} + +typedef enum EXTENDED_OPERAND_TYPE +{ + EXTENDED_OPERAND_EMPTY = 0, + EXTENDED_OPERAND_MODIFIER = 1, +} EXTENDED_OPERAND_TYPE; + +static EXTENDED_OPERAND_TYPE DecodeExtendedOperandType(uint32_t ui32Token) +{ + return (EXTENDED_OPERAND_TYPE)(ui32Token & 0x0000003f); +} + +typedef enum OPERAND_MODIFIER +{ + OPERAND_MODIFIER_NONE = 0, + OPERAND_MODIFIER_NEG = 1, + OPERAND_MODIFIER_ABS = 2, + OPERAND_MODIFIER_ABSNEG = 3, +} OPERAND_MODIFIER; + +static OPERAND_MODIFIER DecodeExtendedOperandModifier(uint32_t ui32Token) +{ + return (OPERAND_MODIFIER)((ui32Token & 0x00003fc0) >> 6); +} + +static const uint32_t GLOBAL_FLAG_REFACTORING_ALLOWED = (1<<11); +static const uint32_t GLOBAL_FLAG_ENABLE_DOUBLE_PRECISION_FLOAT_OPS = (1<<12); +static const uint32_t GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL = (1<<13); +static const uint32_t GLOBAL_FLAG_ENABLE_RAW_AND_STRUCTURED_BUFFERS = (1<<14); +static const uint32_t GLOBAL_FLAG_SKIP_OPTIMIZATION = (1<<15); +static const uint32_t GLOBAL_FLAG_ENABLE_MINIMUM_PRECISION = (1<<16); +static const uint32_t GLOBAL_FLAG_ENABLE_DOUBLE_EXTENSIONS = (1<<17); +static const uint32_t GLOBAL_FLAG_ENABLE_SHADER_EXTENSIONS = (1<<18); + +static uint32_t DecodeGlobalFlags(uint32_t ui32Token) +{ + return (uint32_t)(ui32Token & 0x00fff800); +} + +static INTERPOLATION_MODE DecodeInterpolationMode(uint32_t ui32Token) +{ + return (INTERPOLATION_MODE)((ui32Token & 0x00007800) >> 11); +} + + +typedef enum PRIMITIVE_TOPOLOGY +{ + PRIMITIVE_TOPOLOGY_UNDEFINED = 0, + PRIMITIVE_TOPOLOGY_POINTLIST = 1, + PRIMITIVE_TOPOLOGY_LINELIST = 2, + PRIMITIVE_TOPOLOGY_LINESTRIP = 3, + PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4, + PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5, + // 6 is reserved for legacy triangle fans + // Adjacency values should be equal to (0x8 & non-adjacency): + PRIMITIVE_TOPOLOGY_LINELIST_ADJ = 10, + PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ = 11, + PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ = 12, + PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ = 13, +} PRIMITIVE_TOPOLOGY; + +static PRIMITIVE_TOPOLOGY DecodeGSOutputPrimitiveTopology(uint32_t ui32Token) +{ + return (PRIMITIVE_TOPOLOGY)((ui32Token & 0x0001f800) >> 11); +} + +typedef enum PRIMITIVE +{ + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_POINT = 1, + PRIMITIVE_LINE = 2, + PRIMITIVE_TRIANGLE = 3, + // Adjacency values should be equal to (0x4 & non-adjacency): + PRIMITIVE_LINE_ADJ = 6, + PRIMITIVE_TRIANGLE_ADJ = 7, + PRIMITIVE_1_CONTROL_POINT_PATCH = 8, + PRIMITIVE_2_CONTROL_POINT_PATCH = 9, + PRIMITIVE_3_CONTROL_POINT_PATCH = 10, + PRIMITIVE_4_CONTROL_POINT_PATCH = 11, + PRIMITIVE_5_CONTROL_POINT_PATCH = 12, + PRIMITIVE_6_CONTROL_POINT_PATCH = 13, + PRIMITIVE_7_CONTROL_POINT_PATCH = 14, + PRIMITIVE_8_CONTROL_POINT_PATCH = 15, + PRIMITIVE_9_CONTROL_POINT_PATCH = 16, + PRIMITIVE_10_CONTROL_POINT_PATCH = 17, + PRIMITIVE_11_CONTROL_POINT_PATCH = 18, + PRIMITIVE_12_CONTROL_POINT_PATCH = 19, + PRIMITIVE_13_CONTROL_POINT_PATCH = 20, + PRIMITIVE_14_CONTROL_POINT_PATCH = 21, + PRIMITIVE_15_CONTROL_POINT_PATCH = 22, + PRIMITIVE_16_CONTROL_POINT_PATCH = 23, + PRIMITIVE_17_CONTROL_POINT_PATCH = 24, + PRIMITIVE_18_CONTROL_POINT_PATCH = 25, + PRIMITIVE_19_CONTROL_POINT_PATCH = 26, + PRIMITIVE_20_CONTROL_POINT_PATCH = 27, + PRIMITIVE_21_CONTROL_POINT_PATCH = 28, + PRIMITIVE_22_CONTROL_POINT_PATCH = 29, + PRIMITIVE_23_CONTROL_POINT_PATCH = 30, + PRIMITIVE_24_CONTROL_POINT_PATCH = 31, + PRIMITIVE_25_CONTROL_POINT_PATCH = 32, + PRIMITIVE_26_CONTROL_POINT_PATCH = 33, + PRIMITIVE_27_CONTROL_POINT_PATCH = 34, + PRIMITIVE_28_CONTROL_POINT_PATCH = 35, + PRIMITIVE_29_CONTROL_POINT_PATCH = 36, + PRIMITIVE_30_CONTROL_POINT_PATCH = 37, + PRIMITIVE_31_CONTROL_POINT_PATCH = 38, + PRIMITIVE_32_CONTROL_POINT_PATCH = 39, +} PRIMITIVE; + +static PRIMITIVE DecodeGSInputPrimitive(uint32_t ui32Token) +{ + return (PRIMITIVE)((ui32Token & 0x0001f800) >> 11); +} + +static TESSELLATOR_PARTITIONING DecodeTessPartitioning(uint32_t ui32Token) +{ + return (TESSELLATOR_PARTITIONING)((ui32Token & 0x00003800) >> 11); +} + +typedef enum TESSELLATOR_DOMAIN +{ + TESSELLATOR_DOMAIN_UNDEFINED = 0, + TESSELLATOR_DOMAIN_ISOLINE = 1, + TESSELLATOR_DOMAIN_TRI = 2, + TESSELLATOR_DOMAIN_QUAD = 3 +} TESSELLATOR_DOMAIN; + +static TESSELLATOR_DOMAIN DecodeTessDomain(uint32_t ui32Token) +{ + return (TESSELLATOR_DOMAIN)((ui32Token & 0x00001800) >> 11); +} + +static TESSELLATOR_OUTPUT_PRIMITIVE DecodeTessOutPrim(uint32_t ui32Token) +{ + return (TESSELLATOR_OUTPUT_PRIMITIVE)((ui32Token & 0x00003800) >> 11); +} + +static const uint32_t SYNC_THREADS_IN_GROUP = 0x00000800; +static const uint32_t SYNC_THREAD_GROUP_SHARED_MEMORY = 0x00001000; +static const uint32_t SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP = 0x00002000; +static const uint32_t SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GLOBAL = 0x00004000; + +static uint32_t DecodeSyncFlags(uint32_t ui32Token) +{ + return ui32Token & 0x00007800; +} + +// The number of types that implement this interface +static uint32_t DecodeInterfaceTableLength(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0x0000ffff) >> 0); +} + +// The number of interfaces that are defined in this array. +static uint32_t DecodeInterfaceArrayLength(uint32_t ui32Token) +{ + return (uint32_t)((ui32Token & 0xffff0000) >> 16); +} + +typedef enum CUSTOMDATA_CLASS +{ + CUSTOMDATA_COMMENT = 0, + CUSTOMDATA_DEBUGINFO, + CUSTOMDATA_OPAQUE, + CUSTOMDATA_DCL_IMMEDIATE_CONSTANT_BUFFER, + CUSTOMDATA_SHADER_MESSAGE, +} CUSTOMDATA_CLASS; + +static CUSTOMDATA_CLASS DecodeCustomDataClass(uint32_t ui32Token) +{ + return (CUSTOMDATA_CLASS)((ui32Token & 0xfffff800) >> 11); +} + +static uint32_t DecodeInstructionSaturate(uint32_t ui32Token) +{ + return (ui32Token & 0x00002000) ? 1 : 0; +} + +typedef enum OPERAND_MIN_PRECISION +{ + OPERAND_MIN_PRECISION_DEFAULT = 0, // Default precision + // for the shader model + OPERAND_MIN_PRECISION_FLOAT_16 = 1, // Min 16 bit/component float + OPERAND_MIN_PRECISION_FLOAT_2_8 = 2, // Min 10(2.8)bit/comp. float + OPERAND_MIN_PRECISION_SINT_16 = 4, // Min 16 bit/comp. signed integer + OPERAND_MIN_PRECISION_UINT_16 = 5, // Min 16 bit/comp. unsigned integer +} OPERAND_MIN_PRECISION; + +static uint32_t DecodeOperandMinPrecision(uint32_t ui32Token) +{ + return (ui32Token & 0x0001C000) >> 14; +} + +static uint32_t DecodeOutputControlPointCount(uint32_t ui32Token) +{ + return ((ui32Token & 0x0001f800) >> 11); +} + +typedef enum IMMEDIATE_ADDRESS_OFFSET_COORD +{ + IMMEDIATE_ADDRESS_OFFSET_U = 0, + IMMEDIATE_ADDRESS_OFFSET_V = 1, + IMMEDIATE_ADDRESS_OFFSET_W = 2, +} IMMEDIATE_ADDRESS_OFFSET_COORD; + + +#define IMMEDIATE_ADDRESS_OFFSET_SHIFT(Coord) (9+4*((Coord)&3)) +#define IMMEDIATE_ADDRESS_OFFSET_MASK(Coord) (0x0000000f<>(IMMEDIATE_ADDRESS_OFFSET_SHIFT(eCoord)))); +} + +// UAV access scope flags +static const uint32_t GLOBALLY_COHERENT_ACCESS = 0x00010000; +static uint32_t DecodeAccessCoherencyFlags(uint32_t ui32Token) +{ + return ui32Token & 0x00010000; +} + + +typedef enum RESINFO_RETURN_TYPE +{ + RESINFO_INSTRUCTION_RETURN_FLOAT = 0, + RESINFO_INSTRUCTION_RETURN_RCPFLOAT = 1, + RESINFO_INSTRUCTION_RETURN_UINT = 2 +} RESINFO_RETURN_TYPE; + +static RESINFO_RETURN_TYPE DecodeResInfoReturnType(uint32_t ui32Token) +{ + return (RESINFO_RETURN_TYPE)((ui32Token & 0x00001800) >> 11); +} + +#include "tokensDX9.h" + +#endif diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokensDX9.h b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokensDX9.h new file mode 100644 index 0000000000..1284419ca2 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/internal_includes/tokensDX9.h @@ -0,0 +1,304 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "debug.h" + +static const uint32_t D3D9SHADER_TYPE_VERTEX = 0xFFFE0000; +static const uint32_t D3D9SHADER_TYPE_PIXEL = 0xFFFF0000; + +static SHADER_TYPE DecodeShaderTypeDX9(const uint32_t ui32Token) +{ + uint32_t ui32Type = ui32Token & 0xFFFF0000; + if(ui32Type == D3D9SHADER_TYPE_VERTEX) + return VERTEX_SHADER; + + if(ui32Type == D3D9SHADER_TYPE_PIXEL) + return PIXEL_SHADER; + + return INVALID_SHADER; +} + +static uint32_t DecodeProgramMajorVersionDX9(const uint32_t ui32Token) +{ + return ((ui32Token)>>8)&0xFF; +} + +static uint32_t DecodeProgramMinorVersionDX9(const uint32_t ui32Token) +{ + return ui32Token & 0xFF; +} + +typedef enum +{ + OPCODE_DX9_NOP = 0, + OPCODE_DX9_MOV , + OPCODE_DX9_ADD , + OPCODE_DX9_SUB , + OPCODE_DX9_MAD , + OPCODE_DX9_MUL , + OPCODE_DX9_RCP , + OPCODE_DX9_RSQ , + OPCODE_DX9_DP3 , + OPCODE_DX9_DP4 , + OPCODE_DX9_MIN , + OPCODE_DX9_MAX , + OPCODE_DX9_SLT , + OPCODE_DX9_SGE , + OPCODE_DX9_EXP , + OPCODE_DX9_LOG , + OPCODE_DX9_LIT , + OPCODE_DX9_DST , + OPCODE_DX9_LRP , + OPCODE_DX9_FRC , + OPCODE_DX9_M4x4 , + OPCODE_DX9_M4x3 , + OPCODE_DX9_M3x4 , + OPCODE_DX9_M3x3 , + OPCODE_DX9_M3x2 , + OPCODE_DX9_CALL , + OPCODE_DX9_CALLNZ , + OPCODE_DX9_LOOP , + OPCODE_DX9_RET , + OPCODE_DX9_ENDLOOP , + OPCODE_DX9_LABEL , + OPCODE_DX9_DCL , + OPCODE_DX9_POW , + OPCODE_DX9_CRS , + OPCODE_DX9_SGN , + OPCODE_DX9_ABS , + OPCODE_DX9_NRM , + OPCODE_DX9_SINCOS , + OPCODE_DX9_REP , + OPCODE_DX9_ENDREP , + OPCODE_DX9_IF , + OPCODE_DX9_IFC , + OPCODE_DX9_ELSE , + OPCODE_DX9_ENDIF , + OPCODE_DX9_BREAK , + OPCODE_DX9_BREAKC , + OPCODE_DX9_MOVA , + OPCODE_DX9_DEFB , + OPCODE_DX9_DEFI , + + OPCODE_DX9_TEXCOORD = 64, + OPCODE_DX9_TEXKILL , + OPCODE_DX9_TEX , + OPCODE_DX9_TEXBEM , + OPCODE_DX9_TEXBEML , + OPCODE_DX9_TEXREG2AR , + OPCODE_DX9_TEXREG2GB , + OPCODE_DX9_TEXM3x2PAD , + OPCODE_DX9_TEXM3x2TEX , + OPCODE_DX9_TEXM3x3PAD , + OPCODE_DX9_TEXM3x3TEX , + OPCODE_DX9_RESERVED0 , + OPCODE_DX9_TEXM3x3SPEC , + OPCODE_DX9_TEXM3x3VSPEC , + OPCODE_DX9_EXPP , + OPCODE_DX9_LOGP , + OPCODE_DX9_CND , + OPCODE_DX9_DEF , + OPCODE_DX9_TEXREG2RGB , + OPCODE_DX9_TEXDP3TEX , + OPCODE_DX9_TEXM3x2DEPTH , + OPCODE_DX9_TEXDP3 , + OPCODE_DX9_TEXM3x3 , + OPCODE_DX9_TEXDEPTH , + OPCODE_DX9_CMP , + OPCODE_DX9_BEM , + OPCODE_DX9_DP2ADD , + OPCODE_DX9_DSX , + OPCODE_DX9_DSY , + OPCODE_DX9_TEXLDD , + OPCODE_DX9_SETP , + OPCODE_DX9_TEXLDL , + OPCODE_DX9_BREAKP , + + OPCODE_DX9_PHASE = 0xFFFD, + OPCODE_DX9_COMMENT = 0xFFFE, + OPCODE_DX9_END = 0xFFFF, + + OPCODE_DX9_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} OPCODE_TYPE_DX9; + +static OPCODE_TYPE_DX9 DecodeOpcodeTypeDX9(const uint32_t ui32Token) +{ + return (OPCODE_TYPE_DX9)(ui32Token & 0x0000FFFF); +} + +static uint32_t DecodeInstructionLengthDX9(const uint32_t ui32Token) +{ + return (ui32Token & 0x0F000000)>>24; +} + +static uint32_t DecodeCommentLengthDX9(const uint32_t ui32Token) +{ + return (ui32Token & 0x7FFF0000)>>16; +} + +static uint32_t DecodeOperandRegisterNumberDX9(const uint32_t ui32Token) +{ + return ui32Token & 0x000007FF; +} + +typedef enum +{ + OPERAND_TYPE_DX9_TEMP = 0, // Temporary Register File + OPERAND_TYPE_DX9_INPUT = 1, // Input Register File + OPERAND_TYPE_DX9_CONST = 2, // Constant Register File + OPERAND_TYPE_DX9_ADDR = 3, // Address Register (VS) + OPERAND_TYPE_DX9_TEXTURE = 3, // Texture Register File (PS) + OPERAND_TYPE_DX9_RASTOUT = 4, // Rasterizer Register File + OPERAND_TYPE_DX9_ATTROUT = 5, // Attribute Output Register File + OPERAND_TYPE_DX9_TEXCRDOUT = 6, // Texture Coordinate Output Register File + OPERAND_TYPE_DX9_OUTPUT = 6, // Output register file for VS3.0+ + OPERAND_TYPE_DX9_CONSTINT = 7, // Constant Integer Vector Register File + OPERAND_TYPE_DX9_COLOROUT = 8, // Color Output Register File + OPERAND_TYPE_DX9_DEPTHOUT = 9, // Depth Output Register File + OPERAND_TYPE_DX9_SAMPLER = 10, // Sampler State Register File + OPERAND_TYPE_DX9_CONST2 = 11, // Constant Register File 2048 - 4095 + OPERAND_TYPE_DX9_CONST3 = 12, // Constant Register File 4096 - 6143 + OPERAND_TYPE_DX9_CONST4 = 13, // Constant Register File 6144 - 8191 + OPERAND_TYPE_DX9_CONSTBOOL = 14, // Constant Boolean register file + OPERAND_TYPE_DX9_LOOP = 15, // Loop counter register file + OPERAND_TYPE_DX9_TEMPFLOAT16 = 16, // 16-bit float temp register file + OPERAND_TYPE_DX9_MISCTYPE = 17, // Miscellaneous (single) registers. + OPERAND_TYPE_DX9_LABEL = 18, // Label + OPERAND_TYPE_DX9_PREDICATE = 19, // Predicate register + OPERAND_TYPE_DX9_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} OPERAND_TYPE_DX9; + +static OPERAND_TYPE_DX9 DecodeOperandTypeDX9(const uint32_t ui32Token) +{ + return (OPERAND_TYPE_DX9)(((ui32Token & 0x70000000) >> 28) | + ((ui32Token & 0x00001800) >> 8)); +} + +static uint32_t CreateOperandTokenDX9(const uint32_t ui32RegNum, const OPERAND_TYPE_DX9 eType) +{ + uint32_t ui32Token = ui32RegNum; + ASSERT(ui32RegNum <2048); + ui32Token |= (eType <<28) & 0x70000000; + ui32Token |= (eType <<8) & 0x00001800; + return ui32Token; +} + +typedef enum { + DECLUSAGE_POSITION = 0, + DECLUSAGE_BLENDWEIGHT = 1, + DECLUSAGE_BLENDINDICES = 2, + DECLUSAGE_NORMAL = 3, + DECLUSAGE_PSIZE = 4, + DECLUSAGE_TEXCOORD = 5, + DECLUSAGE_TANGENT = 6, + DECLUSAGE_BINORMAL = 7, + DECLUSAGE_TESSFACTOR = 8, + DECLUSAGE_POSITIONT = 9, + DECLUSAGE_COLOR = 10, + DECLUSAGE_FOG = 11, + DECLUSAGE_DEPTH = 12, + DECLUSAGE_SAMPLE = 13 +} DECLUSAGE_DX9; + +static DECLUSAGE_DX9 DecodeUsageDX9(const uint32_t ui32Token) +{ + return (DECLUSAGE_DX9) (ui32Token & 0x0000000f); +} + +static uint32_t DecodeUsageIndexDX9(const uint32_t ui32Token) +{ + return (ui32Token & 0x000f0000)>>16; +} + +static uint32_t DecodeOperandIsRelativeAddressModeDX9(const uint32_t ui32Token) +{ + return ui32Token & (1<<13); +} + +static const uint32_t DX9_SWIZZLE_SHIFT = 16; +#define NO_SWIZZLE_DX9 ((0<>16); +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/reflect.c b/Code/Tools/HLSLCrossCompilerMETAL/src/reflect.c new file mode 100644 index 0000000000..03f3388a93 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/reflect.c @@ -0,0 +1,1213 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/reflect.h" +#include "internal_includes/debug.h" +#include "internal_includes/decode.h" +#include "internal_includes/hlslcc_malloc.h" +#include "bstrlib.h" +#include +#include + +static void FormatVariableName(char* Name) +{ + /* MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/bb944006(v=vs.85).aspx + The uniform function parameters appear in the + constant table prepended with a dollar sign ($), + unlike the global variables. The dollar sign is + required to avoid name collisions between local + uniform inputs and global variables of the same name.*/ + + /* Leave $ThisPointer, $Element and $Globals as-is. + Otherwise remove $ character ($ is not a valid character for GLSL variable names). */ + if (Name[0] == '$') + { + if (strcmp(Name, "$Element") != 0 && + strcmp(Name, "$Globals") != 0 && + strcmp(Name, "$ThisPointer") != 0) + { + Name[0] = '_'; + } + } +} + +static void ReadStringFromTokenStream(const uint32_t* tokens, char* str) +{ + char* charTokens = (char*) tokens; + char nextCharacter = *charTokens++; + int length = 0; + + //Add each individual character until + //a terminator is found. + while (nextCharacter != 0) + { + str[length++] = nextCharacter; + + if (length > MAX_REFLECT_STRING_LENGTH) + { + str[length - 1] = '\0'; + return; + } + + nextCharacter = *charTokens++; + } + + str[length] = '\0'; +} + +static void ReadInputSignatures(const uint32_t* pui32Tokens, + ShaderInfo* psShaderInfo, + const int extended) +{ + uint32_t i; + + InOutSignature* psSignatures; + const uint32_t* pui32FirstSignatureToken = pui32Tokens; + const uint32_t ui32ElementCount = *pui32Tokens++; + /*const uint32_t ui32Key =*/ *pui32Tokens++; + + psSignatures = hlslcc_malloc(sizeof(InOutSignature) * ui32ElementCount); + psShaderInfo->psInputSignatures = psSignatures; + psShaderInfo->ui32NumInputSignatures = ui32ElementCount; + + for (i = 0; i < ui32ElementCount; ++i) + { + uint32_t ui32ComponentMasks; + InOutSignature* psCurrentSignature = psSignatures + i; + uint32_t ui32SemanticNameOffset; + + psCurrentSignature->ui32Stream = 0; + psCurrentSignature->eMinPrec = MIN_PRECISION_DEFAULT; + + if (extended) + { + psCurrentSignature->ui32Stream = *pui32Tokens++; + } + + ui32SemanticNameOffset = *pui32Tokens++; + psCurrentSignature->ui32SemanticIndex = *pui32Tokens++; + psCurrentSignature->eSystemValueType = (SPECIAL_NAME) *pui32Tokens++; + psCurrentSignature->eComponentType = (INOUT_COMPONENT_TYPE) *pui32Tokens++; + psCurrentSignature->ui32Register = *pui32Tokens++; + + ui32ComponentMasks = *pui32Tokens++; + psCurrentSignature->ui32Mask = ui32ComponentMasks & 0x7F; + //Shows which components are read + psCurrentSignature->ui32ReadWriteMask = (ui32ComponentMasks & 0x7F00) >> 8; + + if (extended) + { + psCurrentSignature->eMinPrec = *pui32Tokens++; + } + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstSignatureToken + ui32SemanticNameOffset), psCurrentSignature->SemanticName); + } +} + +static void ReadOutputSignatures(const uint32_t* pui32Tokens, + ShaderInfo* psShaderInfo, + const int minPrec, + const int streams) +{ + uint32_t i; + + InOutSignature* psSignatures; + const uint32_t* pui32FirstSignatureToken = pui32Tokens; + const uint32_t ui32ElementCount = *pui32Tokens++; + /*const uint32_t ui32Key =*/ *pui32Tokens++; + + psSignatures = hlslcc_malloc(sizeof(InOutSignature) * ui32ElementCount); + psShaderInfo->psOutputSignatures = psSignatures; + psShaderInfo->ui32NumOutputSignatures = ui32ElementCount; + + for (i = 0; i < ui32ElementCount; ++i) + { + uint32_t ui32ComponentMasks; + InOutSignature* psCurrentSignature = psSignatures + i; + uint32_t ui32SemanticNameOffset; + + psCurrentSignature->ui32Stream = 0; + psCurrentSignature->eMinPrec = MIN_PRECISION_DEFAULT; + + if (streams) + { + psCurrentSignature->ui32Stream = *pui32Tokens++; + } + + ui32SemanticNameOffset = *pui32Tokens++; + psCurrentSignature->ui32SemanticIndex = *pui32Tokens++; + psCurrentSignature->eSystemValueType = (SPECIAL_NAME)*pui32Tokens++; + psCurrentSignature->eComponentType = (INOUT_COMPONENT_TYPE) *pui32Tokens++; + psCurrentSignature->ui32Register = *pui32Tokens++; + + // Massage some special inputs/outputs to match the types of GLSL counterparts + if (psCurrentSignature->eSystemValueType == NAME_RENDER_TARGET_ARRAY_INDEX) + { + psCurrentSignature->eComponentType = INOUT_COMPONENT_SINT32; + } + + ui32ComponentMasks = *pui32Tokens++; + psCurrentSignature->ui32Mask = ui32ComponentMasks & 0x7F; + //Shows which components are NEVER written. + psCurrentSignature->ui32ReadWriteMask = (ui32ComponentMasks & 0x7F00) >> 8; + + if (minPrec) + { + psCurrentSignature->eMinPrec = *pui32Tokens++; + } + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstSignatureToken + ui32SemanticNameOffset), psCurrentSignature->SemanticName); + } +} + +static void ReadPatchConstantSignatures(const uint32_t* pui32Tokens, + ShaderInfo* psShaderInfo, + const int minPrec, + const int streams) +{ + uint32_t i; + + InOutSignature* psSignatures; + const uint32_t* pui32FirstSignatureToken = pui32Tokens; + const uint32_t ui32ElementCount = *pui32Tokens++; + /*const uint32_t ui32Key =*/ *pui32Tokens++; + + psSignatures = hlslcc_malloc(sizeof(InOutSignature) * ui32ElementCount); + psShaderInfo->psPatchConstantSignatures = psSignatures; + psShaderInfo->ui32NumPatchConstantSignatures = ui32ElementCount; + + for (i = 0; i < ui32ElementCount; ++i) + { + uint32_t ui32ComponentMasks; + InOutSignature* psCurrentSignature = psSignatures + i; + uint32_t ui32SemanticNameOffset; + + psCurrentSignature->ui32Stream = 0; + psCurrentSignature->eMinPrec = MIN_PRECISION_DEFAULT; + + if (streams) + { + psCurrentSignature->ui32Stream = *pui32Tokens++; + } + + ui32SemanticNameOffset = *pui32Tokens++; + psCurrentSignature->ui32SemanticIndex = *pui32Tokens++; + psCurrentSignature->eSystemValueType = (SPECIAL_NAME)*pui32Tokens++; + psCurrentSignature->eComponentType = (INOUT_COMPONENT_TYPE) *pui32Tokens++; + psCurrentSignature->ui32Register = *pui32Tokens++; + + // Massage some special inputs/outputs to match the types of GLSL counterparts + if (psCurrentSignature->eSystemValueType == NAME_RENDER_TARGET_ARRAY_INDEX) + { + psCurrentSignature->eComponentType = INOUT_COMPONENT_SINT32; + } + + ui32ComponentMasks = *pui32Tokens++; + psCurrentSignature->ui32Mask = ui32ComponentMasks & 0x7F; + //Shows which components are NEVER written. + psCurrentSignature->ui32ReadWriteMask = (ui32ComponentMasks & 0x7F00) >> 8; + + if (minPrec) + { + psCurrentSignature->eMinPrec = *pui32Tokens++; + } + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstSignatureToken + ui32SemanticNameOffset), psCurrentSignature->SemanticName); + } +} + +static const uint32_t* ReadResourceBinding(const uint32_t* pui32FirstResourceToken, const uint32_t* pui32Tokens, ResourceBinding* psBinding) +{ + uint32_t ui32NameOffset = *pui32Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstResourceToken + ui32NameOffset), psBinding->Name); + FormatVariableName(psBinding->Name); + + psBinding->eType = *pui32Tokens++; + psBinding->ui32ReturnType = *pui32Tokens++; + psBinding->eDimension = (REFLECT_RESOURCE_DIMENSION)*pui32Tokens++; + psBinding->ui32NumSamples = *pui32Tokens++; + psBinding->ui32BindPoint = *pui32Tokens++; + psBinding->ui32BindCount = *pui32Tokens++; + psBinding->ui32Flags = *pui32Tokens++; + psBinding->eBindArea = UAVAREA_INVALID; + + return pui32Tokens; +} + +//Read D3D11_SHADER_TYPE_DESC +static void ReadShaderVariableType(const uint32_t ui32MajorVersion, + const uint32_t* pui32FirstConstBufToken, + const uint32_t* pui32tokens, ShaderVarType* varType) +{ + const uint16_t* pui16Tokens = (const uint16_t*) pui32tokens; + uint16_t ui32MemberCount; + uint32_t ui32MemberOffset; + const uint32_t* pui32MemberTokens; + uint32_t i; + + varType->Class = (SHADER_VARIABLE_CLASS)pui16Tokens[0]; + varType->Type = (SHADER_VARIABLE_TYPE)pui16Tokens[1]; + varType->Rows = pui16Tokens[2]; + varType->Columns = pui16Tokens[3]; + varType->Elements = pui16Tokens[4]; + + varType->MemberCount = ui32MemberCount = pui16Tokens[5]; + varType->Members = 0; + + if (varType->ParentCount) + { + ASSERT((strlen(varType->Parent->FullName) + 1 + strlen(varType->Name) + 1 + 2) < MAX_REFLECT_STRING_LENGTH); + + strcpy(varType->FullName, varType->Parent->FullName); + strcat(varType->FullName, "."); + strcat(varType->FullName, varType->Name); + } + + if (ui32MemberCount) + { + varType->Members = (ShaderVarType*)hlslcc_malloc(sizeof(ShaderVarType) * ui32MemberCount); + + ui32MemberOffset = pui32tokens[3]; + + pui32MemberTokens = (const uint32_t*)((const char*)pui32FirstConstBufToken + ui32MemberOffset); + + for (i = 0; i < ui32MemberCount; ++i) + { + uint32_t ui32NameOffset = *pui32MemberTokens++; + uint32_t ui32MemberTypeOffset = *pui32MemberTokens++; + + varType->Members[i].Parent = varType; + varType->Members[i].ParentCount = varType->ParentCount + 1; + + varType->Members[i].Offset = *pui32MemberTokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstConstBufToken + ui32NameOffset), varType->Members[i].Name); + + ReadShaderVariableType(ui32MajorVersion, pui32FirstConstBufToken, + (const uint32_t*)((const char*)pui32FirstConstBufToken + ui32MemberTypeOffset), &varType->Members[i]); + } + } +} + +static const uint32_t* ReadConstantBuffer(ShaderInfo* psShaderInfo, + const uint32_t* pui32FirstConstBufToken, const uint32_t* pui32Tokens, ConstantBuffer* psBuffer) +{ + uint32_t i; + uint32_t ui32NameOffset = *pui32Tokens++; + uint32_t ui32VarCount = *pui32Tokens++; + uint32_t ui32VarOffset = *pui32Tokens++; + const uint32_t* pui32VarToken = (const uint32_t*)((const char*)pui32FirstConstBufToken + ui32VarOffset); + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstConstBufToken + ui32NameOffset), psBuffer->Name); + FormatVariableName(psBuffer->Name); + + psBuffer->ui32NumVars = ui32VarCount; + psBuffer->asVars = hlslcc_malloc(psBuffer->ui32NumVars * sizeof(ShaderVar)); + + for (i = 0; i < ui32VarCount; ++i) + { + //D3D11_SHADER_VARIABLE_DESC + ShaderVar* const psVar = &psBuffer->asVars[i]; + + uint32_t ui32Flags; + uint32_t ui32TypeOffset; + uint32_t ui32DefaultValueOffset; + + ui32NameOffset = *pui32VarToken++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstConstBufToken + ui32NameOffset), psVar->Name); + FormatVariableName(psVar->Name); + + psVar->ui32StartOffset = *pui32VarToken++; + psVar->ui32Size = *pui32VarToken++; + ui32Flags = *pui32VarToken++; + ui32TypeOffset = *pui32VarToken++; + + strcpy(psVar->sType.Name, psVar->Name); + strcpy(psVar->sType.FullName, psVar->Name); + psVar->sType.Parent = 0; + psVar->sType.ParentCount = 0; + psVar->sType.Offset = 0; + + ReadShaderVariableType(psShaderInfo->ui32MajorVersion, pui32FirstConstBufToken, + (const uint32_t*)((const char*)pui32FirstConstBufToken + ui32TypeOffset), &psVar->sType); + + ui32DefaultValueOffset = *pui32VarToken++; + + + if (psShaderInfo->ui32MajorVersion >= 5) + { + /* uint32_t StartTexture = */ *pui32VarToken++; + /* uint32_t TextureSize = */ *pui32VarToken++; + /* uint32_t StartSampler = */ *pui32VarToken++; + /* uint32_t SamplerSize = */ *pui32VarToken++; + } + + psVar->haveDefaultValue = 0; + + if (ui32DefaultValueOffset) + { + const uint32_t ui32NumDefaultValues = psVar->ui32Size / 4; + const uint32_t* pui32DefaultValToken = (const uint32_t*)((const char*)pui32FirstConstBufToken + ui32DefaultValueOffset); + + //Always a sequence of 4-bytes at the moment. + //bool const becomes 0 or 0xFFFFFFFF int, int & float are 4-bytes. + ASSERT(psVar->ui32Size % 4 == 0); + + psVar->haveDefaultValue = 1; + + psVar->pui32DefaultValues = hlslcc_malloc(psVar->ui32Size); + + for (uint32_t ii = 0; ii < ui32NumDefaultValues; ++ii) + { + psVar->pui32DefaultValues[ii] = pui32DefaultValToken[ii]; + } + } + } + + + { + uint32_t ui32Flags; + uint32_t ui32BufferType; + + psBuffer->ui32TotalSizeInBytes = *pui32Tokens++; + psBuffer->blob = 0; + ui32Flags = *pui32Tokens++; + ui32BufferType = *pui32Tokens++; + } + + return pui32Tokens; +} + +static void ReadResources(const uint32_t* pui32Tokens,//in + ShaderInfo* psShaderInfo) //out +{ + ResourceBinding* psResBindings; + ConstantBuffer* psConstantBuffers; + const uint32_t* pui32ConstantBuffers; + const uint32_t* pui32ResourceBindings; + const uint32_t* pui32FirstToken = pui32Tokens; + uint32_t i; + + const uint32_t ui32NumConstantBuffers = *pui32Tokens++; + const uint32_t ui32ConstantBufferOffset = *pui32Tokens++; + + uint32_t ui32NumResourceBindings = *pui32Tokens++; + uint32_t ui32ResourceBindingOffset = *pui32Tokens++; + /*uint32_t ui32ShaderModel =*/ *pui32Tokens++; + /*uint32_t ui32CompileFlags =*/ *pui32Tokens++;//D3DCompile flags? http://msdn.microsoft.com/en-us/library/gg615083(v=vs.85).aspx + + //Resources + pui32ResourceBindings = (const uint32_t*)((const char*)pui32FirstToken + ui32ResourceBindingOffset); + + psResBindings = hlslcc_malloc(sizeof(ResourceBinding) * ui32NumResourceBindings); + + psShaderInfo->ui32NumResourceBindings = ui32NumResourceBindings; + psShaderInfo->psResourceBindings = psResBindings; + + for (i = 0; i < ui32NumResourceBindings; ++i) + { + pui32ResourceBindings = ReadResourceBinding(pui32FirstToken, pui32ResourceBindings, psResBindings + i); + ASSERT(psResBindings[i].ui32BindPoint < MAX_RESOURCE_BINDINGS); + } + + //Constant buffers + pui32ConstantBuffers = (const uint32_t*)((const char*)pui32FirstToken + ui32ConstantBufferOffset); + + psConstantBuffers = hlslcc_malloc(sizeof(ConstantBuffer) * ui32NumConstantBuffers); + + psShaderInfo->ui32NumConstantBuffers = ui32NumConstantBuffers; + psShaderInfo->psConstantBuffers = psConstantBuffers; + + for (i = 0; i < ui32NumConstantBuffers; ++i) + { + pui32ConstantBuffers = ReadConstantBuffer(psShaderInfo, pui32FirstToken, pui32ConstantBuffers, psConstantBuffers + i); + } + + + //Map resource bindings to constant buffers + if (psShaderInfo->ui32NumConstantBuffers) + { + for (i = 0; i < ui32NumResourceBindings; ++i) + { + ResourceGroup eRGroup; + uint32_t cbufIndex = 0; + + eRGroup = ResourceTypeToResourceGroup(psResBindings[i].eType); + + //Find the constant buffer whose name matches the resource at the given resource binding point + for (cbufIndex = 0; cbufIndex < psShaderInfo->ui32NumConstantBuffers; cbufIndex++) + { + if (strcmp(psConstantBuffers[cbufIndex].Name, psResBindings[i].Name) == 0) + { + psShaderInfo->aui32ResourceMap[eRGroup][psResBindings[i].ui32BindPoint] = cbufIndex; + } + } + } + } +} + +static const uint16_t* ReadClassType(const uint32_t* pui32FirstInterfaceToken, const uint16_t* pui16Tokens, ClassType* psClassType) +{ + const uint32_t* pui32Tokens = (const uint32_t*)pui16Tokens; + uint32_t ui32NameOffset = *pui32Tokens; + pui16Tokens += 2; + + psClassType->ui16ID = *pui16Tokens++; + psClassType->ui16ConstBufStride = *pui16Tokens++; + psClassType->ui16Texture = *pui16Tokens++; + psClassType->ui16Sampler = *pui16Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstInterfaceToken + ui32NameOffset), psClassType->Name); + + return pui16Tokens; +} + +static const uint16_t* ReadClassInstance(const uint32_t* pui32FirstInterfaceToken, const uint16_t* pui16Tokens, ClassInstance* psClassInstance) +{ + uint32_t ui32NameOffset = *pui16Tokens++ << 16; + ui32NameOffset |= *pui16Tokens++; + + psClassInstance->ui16ID = *pui16Tokens++; + psClassInstance->ui16ConstBuf = *pui16Tokens++; + psClassInstance->ui16ConstBufOffset = *pui16Tokens++; + psClassInstance->ui16Texture = *pui16Tokens++; + psClassInstance->ui16Sampler = *pui16Tokens++; + + ReadStringFromTokenStream((const uint32_t*)((const char*)pui32FirstInterfaceToken + ui32NameOffset), psClassInstance->Name); + + return pui16Tokens; +} + + +static void ReadInterfaces(const uint32_t* pui32Tokens, + ShaderInfo* psShaderInfo) +{ + uint32_t i; + uint32_t ui32StartSlot; + const uint32_t* pui32FirstInterfaceToken = pui32Tokens; + const uint32_t ui32ClassInstanceCount = *pui32Tokens++; + const uint32_t ui32ClassTypeCount = *pui32Tokens++; + const uint32_t ui32InterfaceSlotRecordCount = *pui32Tokens++; + /*const uint32_t ui32InterfaceSlotCount =*/ *pui32Tokens++; + const uint32_t ui32ClassInstanceOffset = *pui32Tokens++; + const uint32_t ui32ClassTypeOffset = *pui32Tokens++; + const uint32_t ui32InterfaceSlotOffset = *pui32Tokens++; + + const uint16_t* pui16ClassTypes = (const uint16_t*)((const char*)pui32FirstInterfaceToken + ui32ClassTypeOffset); + const uint16_t* pui16ClassInstances = (const uint16_t*)((const char*)pui32FirstInterfaceToken + ui32ClassInstanceOffset); + const uint32_t* pui32InterfaceSlots = (const uint32_t*)((const char*)pui32FirstInterfaceToken + ui32InterfaceSlotOffset); + + const uint32_t* pui32InterfaceSlotTokens = pui32InterfaceSlots; + + ClassType* psClassTypes; + ClassInstance* psClassInstances; + + psClassTypes = hlslcc_malloc(sizeof(ClassType) * ui32ClassTypeCount); + for (i = 0; i < ui32ClassTypeCount; ++i) + { + pui16ClassTypes = ReadClassType(pui32FirstInterfaceToken, pui16ClassTypes, psClassTypes + i); + psClassTypes[i].ui16ID = (uint16_t)i; + } + + psClassInstances = hlslcc_malloc(sizeof(ClassInstance) * ui32ClassInstanceCount); + for (i = 0; i < ui32ClassInstanceCount; ++i) + { + pui16ClassInstances = ReadClassInstance(pui32FirstInterfaceToken, pui16ClassInstances, psClassInstances + i); + } + + //Slots map function table to $ThisPointer cbuffer variable index + ui32StartSlot = 0; + for (i = 0; i < ui32InterfaceSlotRecordCount; ++i) + { + uint32_t k; + + const uint32_t ui32SlotSpan = *pui32InterfaceSlotTokens++; + const uint32_t ui32Count = *pui32InterfaceSlotTokens++; + const uint32_t ui32TypeIDOffset = *pui32InterfaceSlotTokens++; + const uint32_t ui32TableIDOffset = *pui32InterfaceSlotTokens++; + + const uint16_t* pui16TypeID = (const uint16_t*)((const char*)pui32FirstInterfaceToken + ui32TypeIDOffset); + const uint32_t* pui32TableID = (const uint32_t*)((const char*)pui32FirstInterfaceToken + ui32TableIDOffset); + + for (k = 0; k < ui32Count; ++k) + { + psShaderInfo->aui32TableIDToTypeID[*pui32TableID++] = *pui16TypeID++; + } + + ui32StartSlot += ui32SlotSpan; + } + + psShaderInfo->ui32NumClassInstances = ui32ClassInstanceCount; + psShaderInfo->psClassInstances = psClassInstances; + + psShaderInfo->ui32NumClassTypes = ui32ClassTypeCount; + psShaderInfo->psClassTypes = psClassTypes; +} + +void GetConstantBufferFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ShaderInfo* psShaderInfo, ConstantBuffer** ppsConstBuf) +{ + if (psShaderInfo->ui32MajorVersion > 3) + { + *ppsConstBuf = psShaderInfo->psConstantBuffers + psShaderInfo->aui32ResourceMap[eGroup][ui32BindPoint]; + } + else + { + ASSERT(psShaderInfo->ui32NumConstantBuffers == 1); + *ppsConstBuf = psShaderInfo->psConstantBuffers; + } +} + +int GetResourceFromBindingPoint(const ResourceGroup eGroup, uint32_t const ui32BindPoint, const ShaderInfo* psShaderInfo, ResourceBinding** ppsOutBinding) +{ + uint32_t i; + const uint32_t ui32NumBindings = psShaderInfo->ui32NumResourceBindings; + ResourceBinding* psBindings = psShaderInfo->psResourceBindings; + + for (i = 0; i < ui32NumBindings; ++i) + { + if (ResourceTypeToResourceGroup(psBindings[i].eType) == eGroup) + { + if (ui32BindPoint >= psBindings[i].ui32BindPoint && ui32BindPoint < (psBindings[i].ui32BindPoint + psBindings[i].ui32BindCount)) + { + *ppsOutBinding = psBindings + i; + return 1; + } + } + } + + return 0; +} + +int GetInterfaceVarFromOffset(uint32_t ui32Offset, ShaderInfo* psShaderInfo, ShaderVar** ppsShaderVar) +{ + uint32_t i; + ConstantBuffer* psThisPointerConstBuffer = psShaderInfo->psThisPointerConstBuffer; + + const uint32_t ui32NumVars = psThisPointerConstBuffer->ui32NumVars; + + for (i = 0; i < ui32NumVars; ++i) + { + if (ui32Offset >= psThisPointerConstBuffer->asVars[i].ui32StartOffset && + ui32Offset < (psThisPointerConstBuffer->asVars[i].ui32StartOffset + psThisPointerConstBuffer->asVars[i].ui32Size)) + { + *ppsShaderVar = &psThisPointerConstBuffer->asVars[i]; + return 1; + } + } + return 0; +} + +int GetInputSignatureFromRegister(const uint32_t ui32Register, const ShaderInfo* psShaderInfo, InOutSignature** ppsOut) +{ + uint32_t i; + const uint32_t ui32NumVars = psShaderInfo->ui32NumInputSignatures; + + for (i = 0; i < ui32NumVars; ++i) + { + InOutSignature* psInputSignatures = psShaderInfo->psInputSignatures; + if (ui32Register == psInputSignatures[i].ui32Register) + { + *ppsOut = psInputSignatures + i; + return 1; + } + } + return 0; +} + +int GetOutputSignatureFromRegister(const uint32_t currentPhase, + const uint32_t ui32Register, + const uint32_t ui32CompMask, + const uint32_t ui32Stream, + ShaderInfo* psShaderInfo, + InOutSignature** ppsOut) +{ + uint32_t i; + + if (currentPhase == HS_JOIN_PHASE || currentPhase == HS_FORK_PHASE) + { + const uint32_t ui32NumVars = psShaderInfo->ui32NumPatchConstantSignatures; + + for (i = 0; i < ui32NumVars; ++i) + { + InOutSignature* psOutputSignatures = psShaderInfo->psPatchConstantSignatures; + if (ui32Register == psOutputSignatures[i].ui32Register && + (ui32CompMask & psOutputSignatures[i].ui32Mask) && + ui32Stream == psOutputSignatures[i].ui32Stream) + { + *ppsOut = psOutputSignatures + i; + return 1; + } + } + } + else + { + const uint32_t ui32NumVars = psShaderInfo->ui32NumOutputSignatures; + + for (i = 0; i < ui32NumVars; ++i) + { + InOutSignature* psOutputSignatures = psShaderInfo->psOutputSignatures; + if (ui32Register == psOutputSignatures[i].ui32Register && + (ui32CompMask & psOutputSignatures[i].ui32Mask) && + ui32Stream == psOutputSignatures[i].ui32Stream) + { + *ppsOut = psOutputSignatures + i; + return 1; + } + } + } + return 0; +} + +int GetOutputSignatureFromSystemValue(SPECIAL_NAME eSystemValueType, uint32_t ui32SemanticIndex, ShaderInfo* psShaderInfo, InOutSignature** ppsOut) +{ + uint32_t i; + const uint32_t ui32NumVars = psShaderInfo->ui32NumOutputSignatures; + + for (i = 0; i < ui32NumVars; ++i) + { + InOutSignature* psOutputSignatures = psShaderInfo->psOutputSignatures; + if (eSystemValueType == psOutputSignatures[i].eSystemValueType && + ui32SemanticIndex == psOutputSignatures[i].ui32SemanticIndex) + { + *ppsOut = psOutputSignatures + i; + return 1; + } + } + return 0; +} + +static int IsOffsetInType(ShaderVarType* psType, + uint32_t parentOffset, + uint32_t offsetToFind, + const uint32_t* pui32Swizzle, + int32_t* pi32Index, + int32_t* pi32Rebase) +{ + uint32_t thisOffset = parentOffset + psType->Offset; + uint32_t thisSize = psType->Columns * psType->Rows * 4; + + if (psType->Elements) + { + // Everything smaller than vec4 in an array takes the space of vec4, except for the last one + if (thisSize < 4 * 4) + { + thisSize = (4 * 4 * (psType->Elements - 1)) + thisSize; + } + else + { + thisSize *= psType->Elements; + } + } + + //Swizzle can point to another variable. In the example below + //cbUIUpdates.g_uMaxFaces would be cb1[2].z. The scalars are combined + //into vectors. psCBuf->ui32NumVars will be 3. + + // cbuffer cbUIUpdates + // { + // + // float g_fLifeSpan; // Offset: 0 Size: 4 + // float g_fLifeSpanVar; // Offset: 4 Size: 4 [unused] + // float g_fRadiusMin; // Offset: 8 Size: 4 [unused] + // float g_fRadiusMax; // Offset: 12 Size: 4 [unused] + // float g_fGrowTime; // Offset: 16 Size: 4 [unused] + // float g_fStepSize; // Offset: 20 Size: 4 + // float g_fTurnRate; // Offset: 24 Size: 4 + // float g_fTurnSpeed; // Offset: 28 Size: 4 [unused] + // float g_fLeafRate; // Offset: 32 Size: 4 + // float g_fShrinkTime; // Offset: 36 Size: 4 [unused] + // uint g_uMaxFaces; // Offset: 40 Size: 4 + // + // } + + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cbUIUpdates cbuffer NA NA 1 1 + + if (pui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + offsetToFind += 4; + } + else + if (pui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + offsetToFind += 8; + } + else + if (pui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + offsetToFind += 12; + } + + if ((offsetToFind >= thisOffset) && + offsetToFind < (thisOffset + thisSize)) + { + if (psType->Class == SVC_MATRIX_ROWS || + psType->Class == SVC_MATRIX_COLUMNS) + { + //Matrices are treated as arrays of vectors. + pi32Index[0] = (offsetToFind - thisOffset) / 16; + } + //Check for array of scalars or vectors (both take up 16 bytes per element) + else if ((psType->Class == SVC_SCALAR || psType->Class == SVC_VECTOR) && psType->Elements > 1) + { + pi32Index[0] = (offsetToFind - thisOffset) / 16; + } + else if (psType->Class == SVC_VECTOR && psType->Columns > 1) + { + //Check for vector starting at a non-vec4 offset. + + // cbuffer $Globals + // { + // + // float angle; // Offset: 0 Size: 4 + // float2 angle2; // Offset: 4 Size: 8 + // + // } + + //cb0[0].x = angle + //cb0[0].yzyy = angle2.xyxx + + //Rebase angle2 so that .y maps to .x, .z maps to .y + + pi32Rebase[0] = thisOffset % 16; + } + + return 1; + } + return 0; +} + +int GetShaderVarFromOffset(const uint32_t ui32Vec4Offset, + const uint32_t* pui32Swizzle, + ConstantBuffer* psCBuf, + ShaderVarType** ppsShaderVar, + int32_t* pi32Index, + int32_t* pi32Rebase) +{ + uint32_t i; + + uint32_t ui32ByteOffset = ui32Vec4Offset * 16; + + const uint32_t ui32NumVars = psCBuf->ui32NumVars; + + for (i = 0; i < ui32NumVars; ++i) + { + if (psCBuf->asVars[i].sType.Class == SVC_STRUCT) + { + uint32_t m = 0; + + for (m = 0; m < psCBuf->asVars[i].sType.MemberCount; ++m) + { + ShaderVarType* psMember = psCBuf->asVars[i].sType.Members + m; + + ASSERT(psMember->Class != SVC_STRUCT); + + if (IsOffsetInType(psMember, psCBuf->asVars[i].ui32StartOffset, ui32ByteOffset, pui32Swizzle, pi32Index, pi32Rebase)) + { + ppsShaderVar[0] = psMember; + return 1; + } + } + } + else + { + if (IsOffsetInType(&psCBuf->asVars[i].sType, psCBuf->asVars[i].ui32StartOffset, ui32ByteOffset, pui32Swizzle, pi32Index, pi32Rebase)) + { + ppsShaderVar[0] = &psCBuf->asVars[i].sType; + return 1; + } + } + } + return 0; +} + +ResourceGroup ResourceTypeToResourceGroup(ResourceType eType) +{ + switch (eType) + { + case RTYPE_CBUFFER: + return RGROUP_CBUFFER; + + case RTYPE_SAMPLER: + return RGROUP_SAMPLER; + + case RTYPE_TEXTURE: + case RTYPE_BYTEADDRESS: + case RTYPE_STRUCTURED: + return RGROUP_TEXTURE; + + case RTYPE_UAV_RWTYPED: + case RTYPE_UAV_RWSTRUCTURED: + case RTYPE_UAV_RWBYTEADDRESS: + case RTYPE_UAV_APPEND_STRUCTURED: + case RTYPE_UAV_CONSUME_STRUCTURED: + case RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER: + return RGROUP_UAV; + + case RTYPE_TBUFFER: + ASSERT(0); // Need to find out which group this belongs to + return RGROUP_TEXTURE; + } + + ASSERT(0); + return RGROUP_CBUFFER; +} + +void LoadShaderInfo(const uint32_t ui32MajorVersion, + const uint32_t ui32MinorVersion, + const ReflectionChunks* psChunks, + ShaderInfo* psInfo) +{ + const uint32_t* pui32Inputs = psChunks->pui32Inputs; + const uint32_t* pui32Inputs11 = psChunks->pui32Inputs11; + const uint32_t* pui32Resources = psChunks->pui32Resources; + const uint32_t* pui32Interfaces = psChunks->pui32Interfaces; + const uint32_t* pui32Outputs = psChunks->pui32Outputs; + const uint32_t* pui32Outputs11 = psChunks->pui32Outputs11; + const uint32_t* pui32OutputsWithStreams = psChunks->pui32OutputsWithStreams; + const uint32_t* pui32PatchConstants = psChunks->pui32PatchConstants; + + psInfo->eTessOutPrim = TESSELLATOR_OUTPUT_UNDEFINED; + psInfo->eTessPartitioning = TESSELLATOR_PARTITIONING_UNDEFINED; + + psInfo->ui32MajorVersion = ui32MajorVersion; + psInfo->ui32MinorVersion = ui32MinorVersion; + + + if (pui32Inputs) + { + ReadInputSignatures(pui32Inputs, psInfo, 0); + } + if (pui32Inputs11) + { + ReadInputSignatures(pui32Inputs11, psInfo, 1); + } + if (pui32Resources) + { + ReadResources(pui32Resources, psInfo); + } + if (pui32Interfaces) + { + ReadInterfaces(pui32Interfaces, psInfo); + } + if (pui32Outputs) + { + ReadOutputSignatures(pui32Outputs, psInfo, 0, 0); + } + if (pui32Outputs11) + { + ReadOutputSignatures(pui32Outputs11, psInfo, 1, 1); + } + if (pui32OutputsWithStreams) + { + ReadOutputSignatures(pui32OutputsWithStreams, psInfo, 0, 1); + } + if (pui32PatchConstants) + { + ReadPatchConstantSignatures(pui32PatchConstants, psInfo, 0, 0); + } + // if(pui32Effects10Data) + // ReadEffectsData(pui32Effects10Data, psInfo); NOT IMPLEMENTED + + uint32_t i; + for (i = 0; i < psInfo->ui32NumConstantBuffers; ++i) + { + bstring cbufName = bfromcstr(&psInfo->psConstantBuffers[i].Name[0]); + bstring cbufThisPointer = bfromcstr("$ThisPointer"); + if (bstrcmp(cbufName, cbufThisPointer) == 0) + { + psInfo->psThisPointerConstBuffer = &psInfo->psConstantBuffers[i]; + } + } + + for (i = 0; i < MAX_RESOURCE_BINDINGS; ++i) + { + psInfo->aui32SamplerMap[i] = MAX_RESOURCE_BINDINGS; + } +} + +void FreeShaderInfo(ShaderInfo* psShaderInfo) +{ + //Free any default values for constants. + uint32_t cbuf; + for (cbuf = 0; cbuf < psShaderInfo->ui32NumConstantBuffers; ++cbuf) + { + ConstantBuffer* psCBuf = &psShaderInfo->psConstantBuffers[cbuf]; + uint32_t var; + if (psCBuf->ui32NumVars) + { + for (var = 0; var < psCBuf->ui32NumVars; ++var) + { + ShaderVar* psVar = &psCBuf->asVars[var]; + if (psVar->haveDefaultValue) + { + hlslcc_free(psVar->pui32DefaultValues); + } + } + hlslcc_free(psCBuf->asVars); + } + } + hlslcc_free(psShaderInfo->psInputSignatures); + hlslcc_free(psShaderInfo->psResourceBindings); + hlslcc_free(psShaderInfo->psConstantBuffers); + hlslcc_free(psShaderInfo->psClassTypes); + hlslcc_free(psShaderInfo->psClassInstances); + hlslcc_free(psShaderInfo->psOutputSignatures); + hlslcc_free(psShaderInfo->psPatchConstantSignatures); + + psShaderInfo->ui32NumInputSignatures = 0; + psShaderInfo->ui32NumResourceBindings = 0; + psShaderInfo->ui32NumConstantBuffers = 0; + psShaderInfo->ui32NumClassTypes = 0; + psShaderInfo->ui32NumClassInstances = 0; + psShaderInfo->ui32NumOutputSignatures = 0; + psShaderInfo->ui32NumPatchConstantSignatures = 0; +} + +typedef struct ConstantTableD3D9_TAG +{ + uint32_t size; + uint32_t creator; + uint32_t version; + uint32_t constants; + uint32_t constantInfos; + uint32_t flags; + uint32_t target; +} ConstantTableD3D9; + +// These enums match those in d3dx9shader.h. +enum RegisterSet +{ + RS_BOOL, + RS_INT4, + RS_FLOAT4, + RS_SAMPLER, +}; + +enum TypeClass +{ + CLASS_SCALAR, + CLASS_VECTOR, + CLASS_MATRIX_ROWS, + CLASS_MATRIX_COLUMNS, + CLASS_OBJECT, + CLASS_STRUCT, +}; + +enum Type +{ + PT_VOID, + PT_BOOL, + PT_INT, + PT_FLOAT, + PT_STRING, + PT_TEXTURE, + PT_TEXTURE1D, + PT_TEXTURE2D, + PT_TEXTURE3D, + PT_TEXTURECUBE, + PT_SAMPLER, + PT_SAMPLER1D, + PT_SAMPLER2D, + PT_SAMPLER3D, + PT_SAMPLERCUBE, + PT_PIXELSHADER, + PT_VERTEXSHADER, + PT_PIXELFRAGMENT, + PT_VERTEXFRAGMENT, + PT_UNSUPPORTED, +}; +typedef struct ConstantInfoD3D9_TAG +{ + uint32_t name; + uint16_t registerSet; + uint16_t registerIndex; + uint16_t registerCount; + uint16_t reserved; + uint32_t typeInfo; + uint32_t defaultValue; +} ConstantInfoD3D9; + +typedef struct TypeInfoD3D9_TAG +{ + uint16_t typeClass; + uint16_t type; + uint16_t rows; + uint16_t columns; + uint16_t elements; + uint16_t structMembers; + uint32_t structMemberInfos; +} TypeInfoD3D9; + +typedef struct StructMemberInfoD3D9_TAG +{ + uint32_t name; + uint32_t typeInfo; +} StructMemberInfoD3D9; + +void LoadD3D9ConstantTable(const char* data, + ShaderInfo* psInfo) +{ + ConstantTableD3D9* ctab; + uint32_t constNum; + ConstantInfoD3D9* cinfos; + ConstantBuffer* psConstantBuffer; + uint32_t ui32ConstantBufferSize = 0; + uint32_t numResourceBindingsNeeded = 0; + ShaderVar* var; + + ctab = (ConstantTableD3D9*)data; + + cinfos = (ConstantInfoD3D9*) (data + ctab->constantInfos); + + psInfo->ui32NumConstantBuffers++; + + //Only 1 Constant Table in d3d9 + ASSERT(psInfo->ui32NumConstantBuffers == 1); + + psConstantBuffer = hlslcc_malloc(sizeof(ConstantBuffer)); + + psInfo->psConstantBuffers = psConstantBuffer; + + psConstantBuffer->ui32NumVars = 0; + strcpy(psConstantBuffer->Name, "$Globals"); + + //Determine how many resource bindings to create + for (constNum = 0; constNum < ctab->constants; ++constNum) + { + if (cinfos[constNum].registerSet == RS_SAMPLER) + { + ++numResourceBindingsNeeded; + } + } + + psInfo->psResourceBindings = hlslcc_malloc(numResourceBindingsNeeded * sizeof(ResourceBinding)); + + psConstantBuffer->asVars = hlslcc_malloc((ctab->constants - numResourceBindingsNeeded) * sizeof(ShaderVar)); + + var = &psConstantBuffer->asVars[0]; + + for (constNum = 0; constNum < ctab->constants; ++constNum) + { + TypeInfoD3D9* typeInfo = (TypeInfoD3D9*) (data + cinfos[constNum].typeInfo); + + if (cinfos[constNum].registerSet != RS_SAMPLER) + { + strcpy(var->Name, data + cinfos[constNum].name); + FormatVariableName(var->Name); + var->ui32Size = cinfos[constNum].registerCount * 16; + var->ui32StartOffset = cinfos[constNum].registerIndex * 16; + var->haveDefaultValue = 0; + + if (ui32ConstantBufferSize < (var->ui32Size + var->ui32StartOffset)) + { + ui32ConstantBufferSize = var->ui32Size + var->ui32StartOffset; + } + + var->sType.Rows = typeInfo->rows; + var->sType.Columns = typeInfo->columns; + var->sType.Elements = typeInfo->elements; + var->sType.MemberCount = typeInfo->structMembers; + var->sType.Members = 0; + var->sType.Offset = 0; + strcpy(var->sType.FullName, var->Name); + var->sType.Parent = 0; + var->sType.ParentCount = 0; + + switch (typeInfo->typeClass) + { + case CLASS_SCALAR: + { + var->sType.Class = SVC_SCALAR; + break; + } + case CLASS_VECTOR: + { + var->sType.Class = SVC_VECTOR; + break; + } + case CLASS_MATRIX_ROWS: + { + var->sType.Class = SVC_MATRIX_ROWS; + break; + } + case CLASS_MATRIX_COLUMNS: + { + var->sType.Class = SVC_MATRIX_COLUMNS; + break; + } + case CLASS_OBJECT: + { + var->sType.Class = SVC_OBJECT; + break; + } + case CLASS_STRUCT: + { + var->sType.Class = SVC_STRUCT; + break; + } + } + + switch (cinfos[constNum].registerSet) + { + case RS_BOOL: + { + var->sType.Type = SVT_BOOL; + break; + } + case RS_INT4: + { + var->sType.Type = SVT_INT; + break; + } + case RS_FLOAT4: + { + var->sType.Type = SVT_FLOAT; + break; + } + } + + var++; + psConstantBuffer->ui32NumVars++; + } + else + { + //Create a resource if it is sampler in order to replicate the d3d10+ + //method of separating samplers from general constants. + uint32_t ui32ResourceIndex = psInfo->ui32NumResourceBindings++; + ResourceBinding* res = &psInfo->psResourceBindings[ui32ResourceIndex]; + + strcpy(res->Name, data + cinfos[constNum].name); + FormatVariableName(res->Name); + + res->ui32BindPoint = cinfos[constNum].registerIndex; + res->ui32BindCount = cinfos[constNum].registerCount; + res->ui32Flags = 0; + res->ui32NumSamples = 1; + res->ui32ReturnType = 0; + + res->eType = RTYPE_TEXTURE; + + switch (typeInfo->type) + { + case PT_SAMPLER: + case PT_SAMPLER1D: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURE1D; + break; + case PT_SAMPLER2D: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURE2D; + break; + case PT_SAMPLER3D: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURE3D; + break; + case PT_SAMPLERCUBE: + res->eDimension = REFLECT_RESOURCE_DIMENSION_TEXTURECUBE; + break; + } + } + } + psConstantBuffer->ui32TotalSizeInBytes = ui32ConstantBufferSize; +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSL.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSL.c new file mode 100644 index 0000000000..6c9d02015e --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSL.c @@ -0,0 +1,851 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/tokens.h" +#include "internal_includes/structs.h" +#include "internal_includes/decode.h" +#include "stdlib.h" +#include "stdio.h" +#include "bstrlib.h" +#include "internal_includes/toGLSLInstruction.h" +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/toGLSLDeclaration.h" +#include "internal_includes/languages.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" + +#ifndef GL_VERTEX_SHADER_ARB +#define GL_VERTEX_SHADER_ARB 0x8B31 +#endif +#ifndef GL_FRAGMENT_SHADER_ARB +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#endif +#ifndef GL_GEOMETRY_SHADER +#define GL_GEOMETRY_SHADER 0x8DD9 +#endif +#ifndef GL_TESS_EVALUATION_SHADER +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#endif +#ifndef GL_TESS_CONTROL_SHADER +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif +#ifndef GL_COMPUTE_SHADER +#define GL_COMPUTE_SHADER 0x91B9 +#endif + + +HLSLCC_API void HLSLCC_APIENTRY HLSLcc_SetMemoryFunctions(void* (*malloc_override)(size_t),void* (*calloc_override)(size_t,size_t),void (*free_override)(void *),void* (*realloc_override)(void*,size_t)) +{ + hlslcc_malloc = malloc_override; + hlslcc_calloc = calloc_override; + hlslcc_free = free_override; + hlslcc_realloc = realloc_override; +} + +void AddIndentation(HLSLCrossCompilerContext* psContext) +{ + int i; + int indent = psContext->indent; + bstring glsl = *psContext->currentShaderString; + for(i=0; i < indent; ++i) + { + bcatcstr(glsl, " "); + } +} + +void AddVersionDependentCode(HLSLCrossCompilerContext* psContext) +{ + bstring glsl = *psContext->currentShaderString; + + if(psContext->psShader->ui32MajorVersion > 3 && psContext->psShader->eTargetLanguage != LANG_ES_300 && psContext->psShader->eTargetLanguage != LANG_ES_310 && !(psContext->psShader->eTargetLanguage >= LANG_330)) + { + //DX10+ bycode format requires the ability to treat registers + //as raw bits. ES3.0+ has that built-in, also 330 onwards + bcatcstr(glsl,"#extension GL_ARB_shader_bit_encoding : require\n"); + } + + if(!HaveCompute(psContext->psShader->eTargetLanguage)) + { + if(psContext->psShader->eShaderType == COMPUTE_SHADER) + { + bcatcstr(glsl,"#extension GL_ARB_compute_shader : enable\n"); + bcatcstr(glsl,"#extension GL_ARB_shader_storage_buffer_object : enable\n"); + } + } + + if (!HaveAtomicMem(psContext->psShader->eTargetLanguage) || + !HaveAtomicCounter(psContext->psShader->eTargetLanguage)) + { + if( psContext->psShader->aiOpcodeUsed[OPCODE_IMM_ATOMIC_ALLOC] || + psContext->psShader->aiOpcodeUsed[OPCODE_IMM_ATOMIC_CONSUME] || + psContext->psShader->aiOpcodeUsed[OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED]) + { + bcatcstr(glsl,"#extension GL_ARB_shader_atomic_counters : enable\n"); + + bcatcstr(glsl,"#extension GL_ARB_shader_storage_buffer_object : enable\n"); + } + } + + if(!HaveGather(psContext->psShader->eTargetLanguage)) + { + if(psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO_C] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_C]) + { + bcatcstr(glsl,"#extension GL_ARB_texture_gather : enable\n"); + } + } + + if(!HaveGatherNonConstOffset(psContext->psShader->eTargetLanguage)) + { + if(psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO_C] || + psContext->psShader->aiOpcodeUsed[OPCODE_GATHER4_PO]) + { + bcatcstr(glsl,"#extension GL_ARB_gpu_shader5 : enable\n"); + } + } + + if(!HaveQueryLod(psContext->psShader->eTargetLanguage)) + { + if(psContext->psShader->aiOpcodeUsed[OPCODE_LOD]) + { + bcatcstr(glsl,"#extension GL_ARB_texture_query_lod : enable\n"); + } + } + + if(!HaveQueryLevels(psContext->psShader->eTargetLanguage)) + { + if(psContext->psShader->aiOpcodeUsed[OPCODE_RESINFO]) + { + bcatcstr(glsl,"#extension GL_ARB_texture_query_levels : enable\n"); + } + } + + if(!HaveImageLoadStore(psContext->psShader->eTargetLanguage)) + { + if(psContext->psShader->aiOpcodeUsed[OPCODE_STORE_UAV_TYPED] || + psContext->psShader->aiOpcodeUsed[OPCODE_STORE_RAW] || + psContext->psShader->aiOpcodeUsed[OPCODE_STORE_STRUCTURED]) + { + bcatcstr(glsl,"#extension GL_ARB_shader_image_load_store : enable\n"); + bcatcstr(glsl,"#extension GL_ARB_shader_bit_encoding : enable\n"); + } + else + if(psContext->psShader->aiOpcodeUsed[OPCODE_LD_UAV_TYPED] || + psContext->psShader->aiOpcodeUsed[OPCODE_LD_RAW] || + psContext->psShader->aiOpcodeUsed[OPCODE_LD_STRUCTURED]) + { + bcatcstr(glsl,"#extension GL_ARB_shader_image_load_store : enable\n"); + } + } + + //The fragment language has no default precision qualifier for floating point types. + if (psContext->psShader->eShaderType == PIXEL_SHADER && + psContext->psShader->eTargetLanguage == LANG_ES_100 || psContext->psShader->eTargetLanguage == LANG_ES_300 || psContext->psShader->eTargetLanguage == LANG_ES_310) + { + bcatcstr(glsl, "precision highp float;\n"); + } + + /* There is no default precision qualifier for the following sampler types in either the vertex or fragment language: */ + if (psContext->psShader->eTargetLanguage == LANG_ES_300 || psContext->psShader->eTargetLanguage == LANG_ES_310) + { + bcatcstr(glsl, "precision lowp sampler3D;\n"); + bcatcstr(glsl, "precision lowp samplerCubeShadow;\n"); + bcatcstr(glsl, "precision lowp sampler2DShadow;\n"); + bcatcstr(glsl, "precision lowp sampler2DArray;\n"); + bcatcstr(glsl, "precision lowp sampler2DArrayShadow;\n"); + bcatcstr(glsl, "precision lowp isampler2D;\n"); + bcatcstr(glsl, "precision lowp isampler3D;\n"); + bcatcstr(glsl, "precision lowp isamplerCube;\n"); + bcatcstr(glsl, "precision lowp isampler2DArray;\n"); + bcatcstr(glsl, "precision lowp usampler2D;\n"); + bcatcstr(glsl, "precision lowp usampler3D;\n"); + bcatcstr(glsl, "precision lowp usamplerCube;\n"); + bcatcstr(glsl, "precision lowp usampler2DArray;\n"); + + if (psContext->psShader->eTargetLanguage == LANG_ES_310) + { + bcatcstr(glsl, "precision lowp isampler2DMS;\n"); + bcatcstr(glsl, "precision lowp usampler2D;\n"); + bcatcstr(glsl, "precision lowp usampler3D;\n"); + bcatcstr(glsl, "precision lowp usamplerCube;\n"); + bcatcstr(glsl, "precision lowp usampler2DArray;\n"); + bcatcstr(glsl, "precision lowp usampler2DMS;\n"); + bcatcstr(glsl, "precision lowp image2D;\n"); + bcatcstr(glsl, "precision lowp image3D;\n"); + bcatcstr(glsl, "precision lowp imageCube;\n"); + bcatcstr(glsl, "precision lowp image2DArray;\n"); + bcatcstr(glsl, "precision lowp iimage2D;\n"); + bcatcstr(glsl, "precision lowp iimage3D;\n"); + bcatcstr(glsl, "precision lowp iimageCube;\n"); + bcatcstr(glsl, "precision lowp uimage2DArray;\n"); + } + bcatcstr(glsl, "\n"); + } + + if (SubroutinesSupported(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "subroutine void SubroutineType();\n"); + } + + if (psContext->psShader->ui32MajorVersion <= 3) + { + bcatcstr(glsl, "int RepCounter;\n"); + bcatcstr(glsl, "int LoopCounter;\n"); + bcatcstr(glsl, "int ZeroBasedCounter;\n"); + if (psContext->psShader->eShaderType == VERTEX_SHADER) + { + uint32_t texCoord; + bcatcstr(glsl, "ivec4 Address;\n"); + + if (InOutSupported(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "out vec4 OffsetColour;\n"); + bcatcstr(glsl, "out vec4 BaseColour;\n"); + + bcatcstr(glsl, "out vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "out vec4 TexCoord%d;\n", texCoord); + } + } + else + { + bcatcstr(glsl, "varying vec4 OffsetColour;\n"); + bcatcstr(glsl, "varying vec4 BaseColour;\n"); + + bcatcstr(glsl, "varying vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "varying vec4 TexCoord%d;\n", texCoord); + } + } + } + else + { + uint32_t renderTargets, texCoord; + + if (InOutSupported(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "in vec4 OffsetColour;\n"); + bcatcstr(glsl, "in vec4 BaseColour;\n"); + + bcatcstr(glsl, "in vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "in vec4 TexCoord%d;\n", texCoord); + } + } + else + { + bcatcstr(glsl, "varying vec4 OffsetColour;\n"); + bcatcstr(glsl, "varying vec4 BaseColour;\n"); + + bcatcstr(glsl, "varying vec4 Fog;\n"); + + for (texCoord = 0; texCoord < 8; ++texCoord) + { + bformata(glsl, "varying vec4 TexCoord%d;\n", texCoord); + } + } + + if (psContext->psShader->eTargetLanguage > LANG_120) + { + bcatcstr(glsl, "out vec4 outFragData[8];\n"); + for (renderTargets = 0; renderTargets < 8; ++renderTargets) + { + bformata(glsl, "#define Output%d outFragData[%d]\n", renderTargets, renderTargets); + } + } + else if (psContext->psShader->eTargetLanguage >= LANG_ES_300 && psContext->psShader->eTargetLanguage < LANG_120) + { + // ES 3 supports min 4 rendertargets, I guess this is reasonable lower limit for DX9 shaders + bcatcstr(glsl, "out vec4 outFragData[4];\n"); + for (renderTargets = 0; renderTargets < 4; ++renderTargets) + { + bformata(glsl, "#define Output%d outFragData[%d]\n", renderTargets, renderTargets); + } + } + else if (psContext->psShader->eTargetLanguage == LANG_ES_100) + { + bcatcstr(glsl, "#define Output0 gl_FragColor;\n"); + } + else + { + for (renderTargets = 0; renderTargets < 8; ++renderTargets) + { + bformata(glsl, "#define Output%d gl_FragData[%d]\n", renderTargets, renderTargets); + } + } + } + } + + if((psContext->flags & HLSLCC_FLAG_ORIGIN_UPPER_LEFT) + && (psContext->psShader->eTargetLanguage >= LANG_150)) + { + bcatcstr(glsl,"layout(origin_upper_left) in vec4 gl_FragCoord;\n"); + } + + if((psContext->flags & HLSLCC_FLAG_PIXEL_CENTER_INTEGER) + && (psContext->psShader->eTargetLanguage >= LANG_150)) + { + bcatcstr(glsl,"layout(pixel_center_integer) in vec4 gl_FragCoord;\n"); + } + + /* For versions which do not support a vec1 (currently all versions) */ + bcatcstr(glsl,"struct vec1 {\n"); + bcatcstr(glsl,"\tfloat x;\n"); + bcatcstr(glsl,"};\n"); + + if(HaveUVec(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl,"struct uvec1 {\n"); + bcatcstr(glsl,"\tuint x;\n"); + bcatcstr(glsl,"};\n"); + } + + bcatcstr(glsl,"struct ivec1 {\n"); + bcatcstr(glsl,"\tint x;\n"); + bcatcstr(glsl,"};\n"); + + /* + OpenGL 4.1 API spec: + To use any built-in input or output in the gl_PerVertex block in separable + program objects, shader code must redeclare that block prior to use. + */ + if(psContext->psShader->eShaderType == VERTEX_SHADER && psContext->psShader->eTargetLanguage >= LANG_410) + { + bcatcstr(glsl, "out gl_PerVertex {\n"); + bcatcstr(glsl, "vec4 gl_Position;\n"); + bcatcstr(glsl, "float gl_PointSize;\n"); + bcatcstr(glsl, "float gl_ClipDistance[];"); + bcatcstr(glsl, "};\n"); + } +} + +ShaderLang ChooseLanguage(ShaderData* psShader) +{ + // Depends on the HLSL shader model extracted from bytecode. + switch(psShader->ui32MajorVersion) + { + case 5: + { + return LANG_430; + } + case 4: + { + return LANG_330; + } + default: + { + return LANG_120; + } + } +} + +const char* GetVersionString(ShaderLang language) +{ + switch(language) + { + case LANG_ES_100: + { + return "#version 100\n"; + break; + } + case LANG_ES_300: + { + return "#version 300 es\n"; + break; + } + case LANG_ES_310: + { + return "#version 310 es\n"; + break; + } + case LANG_120: + { + return "#version 120\n"; + break; + } + case LANG_130: + { + return "#version 130\n"; + break; + } + case LANG_140: + { + return "#version 140\n"; + break; + } + case LANG_150: + { + return "#version 150\n"; + break; + } + case LANG_330: + { + return "#version 330\n"; + break; + } + case LANG_400: + { + return "#version 400\n"; + break; + } + case LANG_410: + { + return "#version 410\n"; + break; + } + case LANG_420: + { + return "#version 420\n"; + break; + } + case LANG_430: + { + return "#version 430\n"; + break; + } + case LANG_440: + { + return "#version 440\n"; + break; + } + default: + { + return ""; + break; + } + } +} + +void TranslateToGLSL(HLSLCrossCompilerContext* psContext, ShaderLang* planguage,const GlExtensions *extensions) +{ + bstring glsl; + uint32_t i; + ShaderData* psShader = psContext->psShader; + ShaderLang language = *planguage; + uint32_t ui32InstCount = 0; + uint32_t ui32DeclCount = 0; + + psContext->indent = 0; + + /*psShader->sPhase[MAIN_PHASE].ui32InstanceCount = 1; + psShader->sPhase[MAIN_PHASE].ppsDecl = hlslcc_malloc(sizeof(Declaration*)); + psShader->sPhase[MAIN_PHASE].ppsInst = hlslcc_malloc(sizeof(Instruction*)); + psShader->sPhase[MAIN_PHASE].pui32DeclCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->sPhase[MAIN_PHASE].pui32InstCount = hlslcc_malloc(sizeof(uint32_t));*/ + + if(language == LANG_DEFAULT) + { + language = ChooseLanguage(psShader); + *planguage = language; + } + + glsl = bfromcstralloc (1024, GetVersionString(language)); + + psContext->mainShader = glsl; + psContext->earlyMain = bfromcstralloc (1024, ""); + for(i=0; ipostShaderCode[i] = bfromcstralloc (1024, ""); + } + psContext->currentShaderString = &glsl; + psShader->eTargetLanguage = language; + psShader->extensions = (const struct GlExtensions*)extensions; + psContext->currentPhase = MAIN_PHASE; + + if(extensions) + { + if(extensions->ARB_explicit_attrib_location) + bcatcstr(glsl,"#extension GL_ARB_explicit_attrib_location : require\n"); + if(extensions->ARB_explicit_uniform_location) + bcatcstr(glsl,"#extension GL_ARB_explicit_uniform_location : require\n"); + if(extensions->ARB_shading_language_420pack) + bcatcstr(glsl,"#extension GL_ARB_shading_language_420pack : require\n"); + } + + AddVersionDependentCode(psContext); + + if(psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) + { + bcatcstr(glsl, "layout(std140) uniform;\n"); + } + + //Special case. Can have multiple phases. + if(psShader->eShaderType == HULL_SHADER) + { + int haveInstancedForkPhase = 0; // Do we have an instanced fork phase? + int isCurrentForkPhasedInstanced = 0; // Is the current fork phase instanced? + const char* asPhaseFuncNames[NUM_PHASES]; + uint32_t ui32PhaseFuncCallOrder[3]; + uint32_t ui32PhaseCallIndex; + + uint32_t ui32Phase; + uint32_t ui32Instance; + + asPhaseFuncNames[MAIN_PHASE] = ""; + asPhaseFuncNames[HS_GLOBAL_DECL] = ""; + asPhaseFuncNames[HS_FORK_PHASE] = "fork_phase"; + asPhaseFuncNames[HS_CTRL_POINT_PHASE] = "control_point_phase"; + asPhaseFuncNames[HS_JOIN_PHASE] = "join_phase"; + + ConsolidateHullTempVars(psShader); + + for(i=0; i < psShader->asPhase[HS_GLOBAL_DECL].pui32DeclCount[0]; ++i) + { + TranslateDeclaration(psContext, psShader->asPhase[HS_GLOBAL_DECL].ppsDecl[0]+i); + } + + for(ui32Phase=HS_CTRL_POINT_PHASE; ui32PhasecurrentPhase = ui32Phase; + for(ui32Instance = 0; ui32Instance < psShader->asPhase[ui32Phase].ui32InstanceCount; ++ui32Instance) + { + isCurrentForkPhasedInstanced = 0; //reset for each fork phase for cases we don't have a fork phase instance count opcode. + bformata(glsl, "//%s declarations\n", asPhaseFuncNames[ui32Phase]); + for(i=0; i < psShader->asPhase[ui32Phase].pui32DeclCount[ui32Instance]; ++i) + { + TranslateDeclaration(psContext, psShader->asPhase[ui32Phase].ppsDecl[ui32Instance]+i); + if(psShader->asPhase[ui32Phase].ppsDecl[ui32Instance][i].eOpcode == OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT) + { + haveInstancedForkPhase = 1; + isCurrentForkPhasedInstanced = 1; + } + } + + bformata(glsl, "void %s%d()\n{\n", asPhaseFuncNames[ui32Phase], ui32Instance); + psContext->indent++; + + SetDataTypes(psContext, psShader->asPhase[ui32Phase].ppsInst[ui32Instance], psShader->asPhase[ui32Phase].pui32InstCount[ui32Instance]-1); + + if(isCurrentForkPhasedInstanced) + { + AddIndentation(psContext); + bformata(glsl, "for(int forkInstanceID = 0; forkInstanceID < HullPhase%dInstanceCount; ++forkInstanceID) {\n", ui32Instance); + psContext->indent++; + } + + //The minus one here is remove the return statement at end of phases. + //This is needed otherwise the for loop will only run once. + ASSERT(psShader->asPhase[ui32Phase].ppsInst[ui32Instance] [psShader->asPhase[ui32Phase].pui32InstCount[ui32Instance]-1].eOpcode == OPCODE_RET); + for(i=0; i < psShader->asPhase[ui32Phase].pui32InstCount[ui32Instance]-1; ++i) + { + TranslateInstruction(psContext, psShader->asPhase[ui32Phase].ppsInst[ui32Instance]+i, NULL); + } + + if(haveInstancedForkPhase) + { + psContext->indent--; + AddIndentation(psContext); + + if(isCurrentForkPhasedInstanced) + { + bcatcstr(glsl, "}\n"); + } + + if(psContext->havePostShaderCode[psContext->currentPhase]) + { + #ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); + #endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); + #ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); + #endif + } + } + + psContext->indent--; + bcatcstr(glsl, "}\n"); + } + } + + bcatcstr(glsl, "void main()\n{\n"); + + psContext->indent++; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Start Early Main ---\n"); +#endif + bconcat(glsl, psContext->earlyMain); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End Early Main ---\n"); +#endif + + ui32PhaseFuncCallOrder[0] = HS_CTRL_POINT_PHASE; + ui32PhaseFuncCallOrder[1] = HS_FORK_PHASE; + ui32PhaseFuncCallOrder[2] = HS_JOIN_PHASE; + + for(ui32PhaseCallIndex=0; ui32PhaseCallIndex<3; ui32PhaseCallIndex++) + { + ui32Phase = ui32PhaseFuncCallOrder[ui32PhaseCallIndex]; + for(ui32Instance = 0; ui32Instance < psShader->asPhase[ui32Phase].ui32InstanceCount; ++ui32Instance) + { + AddIndentation(psContext); + bformata(glsl, "%s%d();\n", asPhaseFuncNames[ui32Phase], ui32Instance); + + if(ui32Phase == HS_FORK_PHASE) + { + if(psShader->asPhase[HS_JOIN_PHASE].ui32InstanceCount || + (ui32Instance+1 < psShader->asPhase[HS_FORK_PHASE].ui32InstanceCount)) + { + AddIndentation(psContext); + bcatcstr(glsl, "barrier();\n"); + } + } + } + } + + psContext->indent--; + + bcatcstr(glsl, "}\n"); + + return; + } + + + ui32InstCount = psShader->asPhase[MAIN_PHASE].pui32InstCount[0]; + ui32DeclCount = psShader->asPhase[MAIN_PHASE].pui32DeclCount[0]; + + for(i=0; i < ui32DeclCount; ++i) + { + TranslateDeclaration(psContext, psShader->asPhase[MAIN_PHASE].ppsDecl[0]+i); + } + + if(psContext->psShader->ui32NumDx9ImmConst) + { + bformata(psContext->mainShader, "vec4 ImmConstArray [%d];\n", psContext->psShader->ui32NumDx9ImmConst); + } + + bcatcstr(glsl, "void main()\n{\n"); + + psContext->indent++; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Start Early Main ---\n"); +#endif + bconcat(glsl, psContext->earlyMain); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End Early Main ---\n"); +#endif + + MarkIntegerImmediates(psContext); + + SetDataTypes(psContext, psShader->asPhase[MAIN_PHASE].ppsInst[0], ui32InstCount); + + for(i=0; i < ui32InstCount; ++i) + { + TranslateInstruction(psContext, psShader->asPhase[MAIN_PHASE].ppsInst[0]+i, i+1 < ui32InstCount ? psShader->asPhase[MAIN_PHASE].ppsInst[0]+i+1 : 0); + } + + psContext->indent--; + + bcatcstr(glsl, "}\n"); +} + +static void FreeSubOperands(Instruction* psInst, const uint32_t ui32NumInsts) +{ + uint32_t ui32Inst; + for(ui32Inst = 0; ui32Inst < ui32NumInsts; ++ui32Inst) + { + Instruction* psCurrentInst = &psInst[ui32Inst]; + const uint32_t ui32NumOperands = psCurrentInst->ui32NumOperands; + uint32_t ui32Operand; + + for(ui32Operand = 0; ui32Operand < ui32NumOperands; ++ui32Operand) + { + uint32_t ui32SubOperand; + for(ui32SubOperand = 0; ui32SubOperand < MAX_SUB_OPERANDS; ++ui32SubOperand) + { + if(psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand]) + { + hlslcc_free(psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand]); + psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand] = NULL; + } + } + } + } +} + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromMemToGLSL(const char* shader, + unsigned int flags, + ShaderLang language, + const GlExtensions *extensions, + Shader* result) +{ + uint32_t* tokens; + ShaderData* psShader; + char* glslcstr = NULL; + int GLSLShaderType = GL_FRAGMENT_SHADER_ARB; + int success = 0; + uint32_t i; + + tokens = (uint32_t*)shader; + + psShader = DecodeDXBC(tokens); + + if(psShader) + { + HLSLCrossCompilerContext sContext; + + if(psShader->ui32MajorVersion <= 3) + { + flags &= ~HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS; + } + + sContext.psShader = psShader; + sContext.flags = flags; + + for(i=0; ieShaderType) + { + case VERTEX_SHADER: + { + GLSLShaderType = GL_VERTEX_SHADER_ARB; + break; + } + case GEOMETRY_SHADER: + { + GLSLShaderType = GL_GEOMETRY_SHADER; + break; + } + case DOMAIN_SHADER: + { + GLSLShaderType = GL_TESS_EVALUATION_SHADER; + break; + } + case HULL_SHADER: + { + GLSLShaderType = GL_TESS_CONTROL_SHADER; + break; + } + case COMPUTE_SHADER: + { + GLSLShaderType = GL_COMPUTE_SHADER; + break; + } + default: + { + break; + } + } + + glslcstr = bstr2cstr(sContext.mainShader, '\0'); + + bdestroy(sContext.mainShader); + bdestroy(sContext.earlyMain); + for(i=0; iasPhase[i].ppsDecl != 0) + { + uint32_t k; + for(k=0; k < psShader->asPhase[i].ui32InstanceCount; ++k) + { + hlslcc_free(psShader->asPhase[i].ppsDecl[k]); + } + hlslcc_free(psShader->asPhase[i].ppsDecl); + } + if(psShader->asPhase[i].ppsInst != 0) + { + uint32_t k; + for(k=0; k < psShader->asPhase[i].ui32InstanceCount; ++k) + { + FreeSubOperands(psShader->asPhase[i].ppsInst[k], psShader->asPhase[i].pui32InstCount[k]); + hlslcc_free(psShader->asPhase[i].ppsInst[k]); + } + hlslcc_free(psShader->asPhase[i].ppsInst); + } + } + + memcpy(&result->reflection,&psShader->sInfo,sizeof(psShader->sInfo)); + + result->textureSamplerInfo.ui32NumTextureSamplerPairs = psShader->textureSamplerInfo.ui32NumTextureSamplerPairs; + for (i=0; itextureSamplerInfo.ui32NumTextureSamplerPairs; i++) + strcpy(result->textureSamplerInfo.aTextureSamplerPair[i].Name, psShader->textureSamplerInfo.aTextureSamplerPair[i].Name); + + hlslcc_free(psShader); + + success = 1; + } + + shader = 0; + tokens = 0; + + /* Fill in the result struct */ + + result->shaderType = GLSLShaderType; + result->sourceCode = glslcstr; + result->GLSLLanguage = language; + + return success; +} + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromFileToGLSL(const char* filename, + unsigned int flags, + ShaderLang language, + const GlExtensions *extensions, + Shader* result) +{ + FILE* shaderFile; + int length; + size_t readLength; + char* shader; + int success = 0; + + shaderFile = fopen(filename, "rb"); + + if(!shaderFile) + { + return 0; + } + + fseek(shaderFile, 0, SEEK_END); + length = ftell(shaderFile); + fseek(shaderFile, 0, SEEK_SET); + + shader = (char*)hlslcc_malloc(length+1); + + readLength = fread(shader, 1, length, shaderFile); + + fclose(shaderFile); + shaderFile = 0; + + shader[readLength] = '\0'; + + success = TranslateHLSLFromMemToGLSL(shader, flags, language, extensions, result); + + hlslcc_free(shader); + + return success; +} + +HLSLCC_API void HLSLCC_APIENTRY FreeShader(Shader* s) +{ + bcstrfree(s->sourceCode); + s->sourceCode = NULL; + FreeShaderInfo(&s->reflection); +} + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLDeclaration.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLDeclaration.c new file mode 100644 index 0000000000..4d9195339f --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLDeclaration.c @@ -0,0 +1,2678 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "hlslcc.h" +#include "internal_includes/toGLSLDeclaration.h" +#include "internal_includes/toGLSLOperand.h" +#include "internal_includes/languages.h" +#include "bstrlib.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include +#include + +#ifdef _MSC_VER + #ifndef isnan + #define isnan(x) _isnan(x) + #endif + + #ifndef isinf + #define isinf(x) (!_finite(x)) + #endif +#endif + +#define fpcheck(x) (isnan(x) || isinf(x)) + +typedef enum { + GLVARTYPE_FLOAT, + GLVARTYPE_INT, + GLVARTYPE_FLOAT4, +} GLVARTYPE; + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); + +const char* GetTypeString(GLVARTYPE eType) +{ + switch(eType) + { + case GLVARTYPE_FLOAT: + { + return "float"; + } + case GLVARTYPE_INT: + { + return "int"; + } + case GLVARTYPE_FLOAT4: + { + return "vec4"; + } + default: + { + return ""; + } + } +} +const uint32_t GetTypeElementCount(GLVARTYPE eType) +{ + switch(eType) + { + case GLVARTYPE_FLOAT: + case GLVARTYPE_INT: + { + return 1; + } + case GLVARTYPE_FLOAT4: + { + return 4; + } + default: + { + return 0; + } + } +} + +void AddToDx9ImmConstIndexableArray(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + bstring* savedStringPtr = psContext->currentShaderString; + + psContext->currentShaderString = &psContext->earlyMain; + psContext->indent++; + AddIndentation(psContext); + psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber] = psContext->psShader->ui32NumDx9ImmConst; + bformata(psContext->earlyMain, "ImmConstArray[%d] = ", psContext->psShader->ui32NumDx9ImmConst); + TranslateOperand(psContext, psOperand, TO_FLAG_NONE); + bcatcstr(psContext->earlyMain, ";\n"); + psContext->indent--; + psContext->psShader->ui32NumDx9ImmConst++; + + psContext->currentShaderString = savedStringPtr; +} + +void DeclareConstBufferShaderVariable(bstring glsl, const char* Name, const struct ShaderVarType_TAG* psType, int unsizedArray) + //const SHADER_VARIABLE_CLASS eClass, const SHADER_VARIABLE_TYPE eType, + //const char* pszName) +{ + if(psType->Class == SVC_STRUCT) + { + bformata(glsl, "\t%s_Type %s", Name, Name); + } + else if(psType->Class == SVC_MATRIX_COLUMNS || psType->Class == SVC_MATRIX_ROWS) + { + switch(psType->Type) + { + case SVT_FLOAT: + { + bformata(glsl, "\tmat4 %s", Name); + break; + } + default: + { + ASSERT(0); + break; + } + } + if(psType->Elements > 1) + { + bformata(glsl, "[%d]", psType->Elements); + } + } + else + if(psType->Class == SVC_VECTOR) + { + switch(psType->Type) + { + case SVT_FLOAT: + { + bformata(glsl, "\tvec%d %s", psType->Columns, Name); + break; + } + case SVT_UINT: + { + bformata(glsl, "\tuvec%d %s", psType->Columns, Name); + break; + } + case SVT_INT: + { + bformata(glsl, "\tivec%d %s", psType->Columns, Name); + break; + } + case SVT_DOUBLE: + { + bformata(glsl, "\tdvec%d %s", psType->Columns, Name); + break; + } + case SVT_BOOL: + { + bformata(glsl, "\tbvec%d %s", psType->Columns, Name); + break; + } + default: + { + ASSERT(0); + break; + } + } + + if(psType->Elements > 1) + { + bformata(glsl, "[%d]", psType->Elements); + } + } + else + if(psType->Class == SVC_SCALAR) + { + switch(psType->Type) + { + case SVT_FLOAT: + { + bformata(glsl, "\tfloat %s", Name); + break; + } + case SVT_UINT: + { + bformata(glsl, "\tuint %s", Name); + break; + } + case SVT_INT: + { + bformata(glsl, "\tint %s", Name); + break; + } + case SVT_DOUBLE: + { + bformata(glsl, "\tdouble %s", Name); + break; + } + case SVT_BOOL: + { + //Use int instead of bool. + //Allows implicit conversions to integer and + //bool consumes 4-bytes in HLSL and GLSL anyway. + bformata(glsl, "\tint %s", Name); + // Also change the definition in the type tree. + ((ShaderVarType *)psType)->Type = SVT_INT; + break; + } + default: + { + ASSERT(0); + break; + } + } + + if(psType->Elements > 1) + { + bformata(glsl, "[%d]", psType->Elements); + } + } + if(unsizedArray) + bformata(glsl, "[]"); + bformata(glsl, ";\n"); +} + +//In GLSL embedded structure definitions are not supported. +void PreDeclareStructType(bstring glsl, const char* Name, const struct ShaderVarType_TAG* psType) +{ + uint32_t i; + + for(i=0; iMemberCount; ++i) + { + if(psType->Members[i].Class == SVC_STRUCT) + { + PreDeclareStructType(glsl, psType->Members[i].Name, &psType->Members[i]); + } + } + + if(psType->Class == SVC_STRUCT) + { +#if defined(_DEBUG) + uint32_t unnamed_struct = strcmp(Name, "$Element") == 0 ? 1 : 0; +#endif + //Not supported at the moment + ASSERT(!unnamed_struct); + + bformata(glsl, "struct %s_Type {\n", Name); + + for(i=0; iMemberCount; ++i) + { + ASSERT(psType->Members != 0); + + DeclareConstBufferShaderVariable(glsl, psType->Members[i].Name, &psType->Members[i], 0); + } + + bformata(glsl, "};\n"); + } +} + +const char* GetDeclaredInputName(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand) +{ + bstring inputName; + char* cstr; + InOutSignature* psIn; + int found = GetInputSignatureFromRegister(psOperand->ui32RegisterNumber, &psContext->psShader->sInfo, &psIn); + + if((psContext->flags & HLSLCC_FLAG_INOUT_SEMANTIC_NAMES) && found) + { + if (eShaderType == VERTEX_SHADER) /* We cannot have input and output names conflict, but vs output must match ps input. Prefix vs input. */ + inputName = bformat("in_%s%d", psIn->SemanticName, psIn->ui32SemanticIndex); + else + inputName = bformat("%s%d", psIn->SemanticName, psIn->ui32SemanticIndex); + } + else if(eShaderType == GEOMETRY_SHADER) + { + inputName = bformat("VtxOutput%d", psOperand->ui32RegisterNumber); + } + else if(eShaderType == HULL_SHADER) + { + inputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + else if(eShaderType == DOMAIN_SHADER) + { + inputName = bformat("HullOutput%d", psOperand->ui32RegisterNumber); + } + else if(eShaderType == PIXEL_SHADER) + { + if(psContext->flags & HLSLCC_FLAG_TESS_ENABLED) + { + inputName = bformat("DomOutput%d", psOperand->ui32RegisterNumber); + } + else + { + inputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + } + else + { + ASSERT(eShaderType == VERTEX_SHADER); + inputName = bformat("dcl_Input%d", psOperand->ui32RegisterNumber); + } + if((psContext->flags & HLSLCC_FLAG_INOUT_APPEND_SEMANTIC_NAMES) && found) + { + bformata(inputName,"_%s%d", psIn->SemanticName, psIn->ui32SemanticIndex); + } + + cstr = bstr2cstr(inputName, '\0'); + bdestroy(inputName); + return cstr; +} + +const char* GetDeclaredOutputName(const HLSLCrossCompilerContext* psContext, + const SHADER_TYPE eShaderType, + const Operand* psOperand, + int* piStream) +{ + bstring outputName; + char* cstr; + InOutSignature* psOut; + +#if defined(_DEBUG) + int foundOutput = +#endif + GetOutputSignatureFromRegister( + psContext->currentPhase, + psOperand->ui32RegisterNumber, + psOperand->ui32CompMask, + psContext->psShader->ui32CurrentVertexOutputStream, + &psContext->psShader->sInfo, + &psOut); + + ASSERT(foundOutput); + + if(psContext->flags & HLSLCC_FLAG_INOUT_SEMANTIC_NAMES) + { + outputName = bformat("%s%d", psOut->SemanticName, psOut->ui32SemanticIndex); + } + else if(eShaderType == GEOMETRY_SHADER) + { + if(psOut->ui32Stream != 0) + { + outputName = bformat("VtxGeoOutput%d_S%d", psOperand->ui32RegisterNumber, psOut->ui32Stream); + piStream[0] = psOut->ui32Stream; + } + else + { + outputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + + } + else if(eShaderType == DOMAIN_SHADER) + { + outputName = bformat("DomOutput%d", psOperand->ui32RegisterNumber); + } + else if(eShaderType == VERTEX_SHADER) + { + if(psContext->flags & HLSLCC_FLAG_GS_ENABLED) + { + outputName = bformat("VtxOutput%d", psOperand->ui32RegisterNumber); + } + else + { + outputName = bformat("VtxGeoOutput%d", psOperand->ui32RegisterNumber); + } + } + else if(eShaderType == PIXEL_SHADER) + { + outputName = bformat("PixOutput%d", psOperand->ui32RegisterNumber); + } + else + { + ASSERT(eShaderType == HULL_SHADER); + outputName = bformat("HullOutput%d", psOperand->ui32RegisterNumber); + } + if(psContext->flags & HLSLCC_FLAG_INOUT_APPEND_SEMANTIC_NAMES) + { + bformata(outputName, "_%s%d", psOut->SemanticName, psOut->ui32SemanticIndex); + } + + cstr = bstr2cstr(outputName, '\0'); + bdestroy(outputName); + return cstr; +} + +const char* GetInterpolationString(INTERPOLATION_MODE eMode) +{ + switch(eMode) + { + case INTERPOLATION_CONSTANT: + { + return "flat"; + } + case INTERPOLATION_LINEAR: + { + return ""; + } + case INTERPOLATION_LINEAR_CENTROID: + { + return "centroid"; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE: + { + return "noperspective"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID: + { + return "noperspective centroid"; + } + case INTERPOLATION_LINEAR_SAMPLE: + { + return "sample"; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE: + { + return "noperspective sample"; + } + default: + { + return ""; + } + } +} + +static void DeclareInput( + HLSLCrossCompilerContext* psContext, + const Declaration* psDecl, + const char* Interpolation, const char* StorageQualifier, const char* Precision, int iNumComponents, OPERAND_INDEX_DIMENSION eIndexDim, const char* InputName) +{ + ShaderData* psShader = psContext->psShader; + bstring glsl = *psContext->currentShaderString; + + // This falls within the specified index ranges. The default is 0 if no input range is specified + if(psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + return; + + if(psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == 0) + { + const char* vecType = "vec"; + const char* scalarType = "float"; + InOutSignature* psSignature = NULL; + + if( GetInputSignatureFromRegister(psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psSignature) ) + { + switch(psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + vecType = "uvec"; + scalarType = "uint"; + break; + } + case INOUT_COMPONENT_SINT32: + { + vecType = "ivec"; + scalarType = "int"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + } + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags) || + (psShader->eShaderType == VERTEX_SHADER && HaveLimitedInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->flags))) + { + // Skip location if requested by the flags. + if (!(psContext->flags & HLSLCC_FLAG_DISABLE_EXPLICIT_LOCATIONS)) + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + switch(eIndexDim) + { + case INDEX_2D: + { + if(iNumComponents == 1) + { + const uint32_t arraySize = psDecl->asOperands[0].aui32ArraySizes[0]; + + psContext->psShader->abScalarInput[psDecl->asOperands[0].ui32RegisterNumber] = -1; + + bformata(glsl, "%s %s %s %s [%d];\n", StorageQualifier, Precision, scalarType, InputName, + arraySize); + + bformata(glsl, "%s1 Input%d;\n", vecType, psDecl->asOperands[0].ui32RegisterNumber); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = arraySize; + } + else + { + bformata(glsl, "%s %s %s%d %s [%d];\n", StorageQualifier, Precision, vecType, iNumComponents, InputName, + psDecl->asOperands[0].aui32ArraySizes[0]); + + bformata(glsl, "%s%d Input%d[%d];\n", vecType, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber, + psDecl->asOperands[0].aui32ArraySizes[0]); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->asOperands[0].aui32ArraySizes[0]; + } + break; + } + default: + { + + if(psDecl->asOperands[0].eType == OPERAND_TYPE_SPECIAL_TEXCOORD) + { + InputName = "TexCoord"; + } + + if(iNumComponents == 1) + { + psContext->psShader->abScalarInput[psDecl->asOperands[0].ui32RegisterNumber] = 1; + + bformata(glsl, "%s %s %s %s %s;\n", Interpolation, StorageQualifier, Precision, scalarType, InputName); + bformata(glsl, "%s1 Input%d;\n", vecType, psDecl->asOperands[0].ui32RegisterNumber); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = -1; + } + else + { + if(psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] > 0) + { + bformata(glsl, "%s %s %s %s%d %s", Interpolation, StorageQualifier, Precision, vecType, iNumComponents, InputName); + bformata(glsl, "[%d];\n", psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]); + + bformata(glsl, "%s%d Input%d[%d];\n", vecType, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber, + psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]); + + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]; + } + else + { + bformata(glsl, "%s %s %s %s%d %s;\n", Interpolation, StorageQualifier, Precision, vecType, iNumComponents, InputName); + bformata(glsl, "%s%d Input%d;\n", vecType, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = -1; + } + } + break; + } + } + } + + if(psShader->abInputReferencedByInstruction[psDecl->asOperands[0].ui32RegisterNumber]) + { + psContext->currentShaderString = &psContext->earlyMain; + psContext->indent++; + + if(psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == -1) //Not an array + { + AddIndentation(psContext); + bformata(psContext->earlyMain, "Input%d = %s;\n", psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + else + { + int arrayIndex = psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber]; + + while(arrayIndex) + { + AddIndentation(psContext); + bformata(psContext->earlyMain, "Input%d[%d] = %s[%d];\n", psDecl->asOperands[0].ui32RegisterNumber, arrayIndex-1, + InputName, arrayIndex-1); + + arrayIndex--; + } + } + psContext->indent--; + psContext->currentShaderString = &psContext->mainShader; + } +} + +void AddBuiltinInput(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, const char* builtinName) +{ + bstring glsl = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + if(psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == 0) + { + SHADER_VARIABLE_TYPE eType = GetOperandDataType(psContext, &psDecl->asOperands[0]); + switch(eType) + { + case SVT_INT: + bformata(glsl, "ivec4 "); + break; + case SVT_UINT: + bformata(glsl, "uvec4 "); + break; + case SVT_BOOL: + bformata(glsl, "bvec4 "); + break; + default: + bformata(glsl, "vec4 "); + break; + } + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bformata(glsl, ";\n"); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = 1; + } + else + { + //This register has already been declared. The HLSL bytecode likely looks + //something like this then: + // dcl_input_ps constant v3.x + // dcl_input_ps_sgv v3.y, primitive_id + + //GLSL does not allow assignment to a varying! + } + + psContext->currentShaderString = &psContext->earlyMain; + psContext->indent++; + AddIndentation(psContext); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_DESTINATION); + + bformata(psContext->earlyMain, " = %s", builtinName); + + switch(psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + TranslateOperandSwizzle(psContext, &psDecl->asOperands[0]); + break; + default: + //Scalar built-in. Don't apply swizzle. + break; + } + bcatcstr(psContext->earlyMain, ";\n"); + + psContext->indent--; + psContext->currentShaderString = &psContext->mainShader; +} + +int OutputNeedsDeclaring(HLSLCrossCompilerContext* psContext, const Operand* psOperand, const int count) +{ + ShaderData* psShader = psContext->psShader; + const uint32_t declared = ((psContext->currentPhase + 1) << 3) | psShader->ui32CurrentVertexOutputStream; + if(psShader->aiOutputDeclared[psOperand->ui32RegisterNumber] != declared) + { + int offset; + + for(offset = 0; offset < count; offset++) + { + psShader->aiOutputDeclared[psOperand->ui32RegisterNumber+offset] = declared; + } + return 1; + } + + if(psShader->eShaderType == PIXEL_SHADER) + { + if(psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL) + { + return 1; + } + } + + return 0; +} + +void AddBuiltinOutput(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, const GLVARTYPE type, int arrayElements, const char* builtinName) +{ + bstring glsl = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + psContext->havePostShaderCode[psContext->currentPhase] = 1; + + if(OutputNeedsDeclaring(psContext, &psDecl->asOperands[0], arrayElements ? arrayElements : 1)) + { + InOutSignature* psSignature = NULL; + + GetOutputSignatureFromRegister( + psContext->currentPhase, + psDecl->asOperands[0].ui32RegisterNumber, + psDecl->asOperands[0].ui32CompMask, + 0, + &psShader->sInfo, &psSignature); + + bcatcstr(glsl, "#undef "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "\n"); + + bcatcstr(glsl, "#define "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bformata(glsl, " phase%d_", psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "\n"); + + switch (type) + { + case GLVARTYPE_INT: + bcatcstr(glsl, "ivec4 "); + break; + default: + bcatcstr(glsl, "vec4 "); + } + + bformata(glsl, "phase%d_", psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + if(arrayElements) + bformata(glsl, "[%d];\n", arrayElements); + else + bcatcstr(glsl, ";\n"); + + psContext->currentShaderString = &psContext->postShaderCode[psContext->currentPhase]; + glsl = *psContext->currentShaderString; + psContext->indent++; + if(arrayElements) + { + int elem; + for(elem = 0; elem < arrayElements; elem++) + { + AddIndentation(psContext); + bformata(glsl, "%s[%d] = %s(phase%d_", builtinName, elem, GetTypeString(type), psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NAME_ONLY); + bformata(glsl, "[%d]", elem); + TranslateOperandSwizzle(psContext, &psDecl->asOperands[0]); + bformata(glsl, ");\n"); + } + } + else + { + + if(psDecl->asOperands[0].eSpecialName == NAME_CLIP_DISTANCE) + { + int max = GetMaxComponentFromComponentMask(&psDecl->asOperands[0]); + + int applySiwzzle = GetNumSwizzleElements(&psDecl->asOperands[0]) > 1 ? 1 : 0; + int index; + int i; + int multiplier = 1; + char* swizzle[] = {".x", ".y", ".z", ".w"}; + + ASSERT(psSignature!=NULL); + + index = psSignature->ui32SemanticIndex; + + //Clip distance can be spread across 1 or 2 outputs (each no more than a vec4). + //Some examples: + //float4 clip[2] : SV_ClipDistance; //8 clip distances + //float3 clip[2] : SV_ClipDistance; //6 clip distances + //float4 clip : SV_ClipDistance; //4 clip distances + //float clip : SV_ClipDistance; //1 clip distance. + + //In GLSL the clip distance built-in is an array of up to 8 floats. + //So vector to array conversion needs to be done here. + if(index == 1) + { + InOutSignature* psFirstClipSignature; + if(GetOutputSignatureFromSystemValue(NAME_CLIP_DISTANCE, 1, &psShader->sInfo, &psFirstClipSignature)) + { + if(psFirstClipSignature->ui32Mask & (1 << 3)) + { + multiplier = 4; + } + else + if(psFirstClipSignature->ui32Mask & (1 << 2)) + { + multiplier = 3; + } + else + if(psFirstClipSignature->ui32Mask & (1 << 1)) + { + multiplier = 2; + } + } + } + + for(i=0; icurrentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + if(applySiwzzle) + { + bformata(glsl, ")%s;\n", swizzle[i]); + } + else + { + bformata(glsl, ");\n"); + } + } + } + else + { + uint32_t elements = GetNumSwizzleElements(&psDecl->asOperands[0]); + + if(elements != GetTypeElementCount(type)) + { + //This is to handle float3 position seen in control point phases + //struct HS_OUTPUT + //{ + // float3 vPosition : POSITION; + //}; -> dcl_output o0.xyz + //gl_Position is vec4. + AddIndentation(psContext); + bformata(glsl, "%s = %s(phase%d_", builtinName, GetTypeString(type), psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, ", 1);\n"); + } + else + { + AddIndentation(psContext); + bformata(glsl, "%s = %s(phase%d_", builtinName, GetTypeString(type), psContext->currentPhase); + TranslateOperand(psContext, &psDecl->asOperands[0], type == GLVARTYPE_INT ? TO_FLAG_INTEGER : TO_FLAG_NONE); + bformata(glsl, ");\n"); + } + } + } + psContext->indent--; + psContext->currentShaderString = &psContext->mainShader; + } +} + +void AddUserOutput(HLSLCrossCompilerContext* psContext, const Declaration* psDecl) +{ + bstring glsl = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + if(OutputNeedsDeclaring(psContext, &psDecl->asOperands[0], 1)) + { + const Operand* psOperand = &psDecl->asOperands[0]; + const char* Precision = ""; + const char* type = "vec"; + + InOutSignature* psSignature = NULL; + + GetOutputSignatureFromRegister( + psContext->currentPhase, + psDecl->asOperands[0].ui32RegisterNumber, + psDecl->asOperands[0].ui32CompMask, + psShader->ui32CurrentVertexOutputStream, + &psShader->sInfo, + &psSignature); + + switch(psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + type = "uvec"; + break; + } + case INOUT_COMPONENT_SINT32: + { + type = "ivec"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + + if(HavePrecisionQualifers(psShader->eTargetLanguage)) + { + switch(psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + { + Precision = "highp"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_2_8: + { + Precision = "lowp"; + break; + } + case OPERAND_MIN_PRECISION_SINT_16: + { + Precision = "mediump"; + //type = "ivec"; + break; + } + case OPERAND_MIN_PRECISION_UINT_16: + { + Precision = "mediump"; + //type = "uvec"; + break; + } + } + } + + switch(psShader->eShaderType) + { + case PIXEL_SHADER: + { + switch(psDecl->asOperands[0].eType) + { + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + case OPERAND_TYPE_OUTPUT_DEPTH: + { + + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + { + bcatcstr(glsl, "#ifdef GL_ARB_conservative_depth\n"); + bcatcstr(glsl, "#extension GL_ARB_conservative_depth : enable\n"); + bcatcstr(glsl, "layout (depth_greater) out float gl_FragDepth;\n"); + bcatcstr(glsl, "#endif\n"); + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + { + bcatcstr(glsl, "#ifdef GL_ARB_conservative_depth\n"); + bcatcstr(glsl, "#extension GL_ARB_conservative_depth : enable\n"); + bcatcstr(glsl, "layout (depth_less) out float gl_FragDepth;\n"); + bcatcstr(glsl, "#endif\n"); + break; + } + default: + { + if(WriteToFragData(psContext->psShader->eTargetLanguage)) + { + bformata(glsl, "#define Output%d gl_FragData[%d]\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber); + } + else + { + int stream = 0; + const char* OutputName = GetDeclaredOutputName(psContext, PIXEL_SHADER, psOperand, &stream); + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags) || HaveLimitedInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->flags)) + { + uint32_t index = 0; + uint32_t renderTarget = psDecl->asOperands[0].ui32RegisterNumber; + + if((psContext->flags & HLSLCC_FLAG_DUAL_SOURCE_BLENDING) && DualSourceBlendSupported(psContext->psShader->eTargetLanguage)) + { + if(renderTarget > 0) + { + renderTarget = 0; + index = 1; + } + bformata(glsl, "layout(location = %d, index = %d) ", renderTarget, index); + } + else + { + bformata(glsl, "layout(location = %d) ", renderTarget); + } + } + + bformata(glsl, "out %s %s4 %s;\n", Precision, type, OutputName); + if(stream) + { + bformata(glsl, "#define Output%d_S%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, stream, OutputName); + } + else + { + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + } + } + break; + } + } + break; + } + case VERTEX_SHADER: + { + int iNumComponents = 4;//GetMaxComponentFromComponentMask(&psDecl->asOperands[0]); + const char* Interpolation = ""; + int stream = 0; + const char* OutputName = GetDeclaredOutputName(psContext, VERTEX_SHADER, psOperand, &stream); + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + { + if (!(psContext->flags & HLSLCC_FLAG_DISABLE_EXPLICIT_LOCATIONS)) + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + if(InOutSupported(psContext->psShader->eTargetLanguage)) + { + bformata(glsl, "%s out %s %s%d %s;\n", Interpolation, Precision, type, iNumComponents, OutputName); + } + else + { + bformata(glsl, "%s varying %s %s%d %s;\n", Interpolation, Precision, type, iNumComponents, OutputName); + } + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + + break; + } + case GEOMETRY_SHADER: + { + int stream = 0; + const char* OutputName = GetDeclaredOutputName(psContext, GEOMETRY_SHADER, psOperand, &stream); + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + bformata(glsl, "out %s4 %s;\n", type, OutputName); + if(stream) + { + bformata(glsl, "#define Output%d_S%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, stream, OutputName); + } + else + { + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + } + break; + } + case HULL_SHADER: + { + int stream = 0; + const char* OutputName = GetDeclaredOutputName(psContext, HULL_SHADER, psOperand, &stream); + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber!=0);//Reg 0 should be gl_out[gl_InvocationID].gl_Position. + + if(psContext->currentPhase == HS_JOIN_PHASE) + { + bformata(glsl, "out patch %s4 %s[];\n", type, OutputName); + } + else + { + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + bformata(glsl, "out %s4 %s[];\n", type, OutputName); + } + bformata(glsl, "#define Output%d %s[gl_InvocationID]\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + break; + } + case DOMAIN_SHADER: + { + int stream = 0; + const char* OutputName = GetDeclaredOutputName(psContext, DOMAIN_SHADER, psOperand, &stream); + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + { + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + bformata(glsl, "out %s4 %s;\n", type, OutputName); + bformata(glsl, "#define Output%d %s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + break; + } + } + } + else + { + /* + Multiple outputs can be packed into one register. e.g. + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------- ------ + // FACTOR 0 x 3 NONE int x + // MAX 0 y 3 NONE int y + + We want unique outputs to make it easier to use transform feedback. + + out ivec4 FACTOR0; + #define Output3 FACTOR0 + out ivec4 MAX0; + + MAIN SHADER CODE. Writes factor and max to Output3 which aliases FACTOR0. + + MAX0.x = FACTOR0.y; + + This unpacking of outputs is only done when using HLSLCC_FLAG_INOUT_SEMANTIC_NAMES/HLSLCC_FLAG_INOUT_APPEND_SEMANTIC_NAMES. + When not set the application will be using HLSL reflection information to discover + what the input and outputs mean if need be. + */ + + // + + if((psContext->flags & (HLSLCC_FLAG_INOUT_SEMANTIC_NAMES|HLSLCC_FLAG_INOUT_APPEND_SEMANTIC_NAMES)) && (psDecl->asOperands[0].eType == OPERAND_TYPE_OUTPUT)) + { + const Operand* psOperand = &psDecl->asOperands[0]; + InOutSignature* psSignature = NULL; + const char* type = "vec"; + int stream = 0; + const char* OutputName = GetDeclaredOutputName(psContext, psShader->eShaderType, psOperand, &stream); + + GetOutputSignatureFromRegister( + psContext->currentPhase, + psOperand->ui32RegisterNumber, + psOperand->ui32CompMask, + 0, + &psShader->sInfo, + &psSignature); + + if (HaveInOutLocationQualifier(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + { + if (!((psShader->eShaderType == VERTEX_SHADER) && (psContext->flags & HLSLCC_FLAG_DISABLE_EXPLICIT_LOCATIONS))) + bformata(glsl, "layout(location = %d) ", psDecl->asOperands[0].ui32RegisterNumber); + } + + switch(psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + type = "uvec"; + break; + } + case INOUT_COMPONENT_SINT32: + { + type = "ivec"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + bformata(glsl, "out %s4 %s;\n", type, OutputName); + + psContext->havePostShaderCode[psContext->currentPhase] = 1; + + psContext->currentShaderString = &psContext->postShaderCode[psContext->currentPhase]; + glsl = *psContext->currentShaderString; + + bcatcstr(glsl, OutputName); + AddSwizzleUsingElementCount(psContext, GetNumSwizzleElements(psOperand)); + bformata(glsl, " = Output%d", psOperand->ui32RegisterNumber); + TranslateOperandSwizzle(psContext, psOperand); + bcatcstr(glsl, ";\n"); + + psContext->currentShaderString = &psContext->mainShader; + glsl = *psContext->currentShaderString; + } + } +} + +void DeclareUBOConstants(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, + ConstantBuffer* psCBuf, + bstring glsl) +{ + uint32_t i; + const char* Name = psCBuf->Name; + if(psCBuf->Name[0] == '$') //For $Globals + { + Name++; + } + + for(i=0; i < psCBuf->ui32NumVars; ++i) + { + PreDeclareStructType(glsl, + psCBuf->asVars[i].Name, + &psCBuf->asVars[i].sType); + } + + /* [layout (location = X)] uniform vec4 HLSLConstantBufferName[numConsts]; */ + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + bformata(glsl, "layout(binding = %d) ", ui32BindingPoint); + + bformata(glsl, "uniform %s {\n ", Name); + + for(i=0; i < psCBuf->ui32NumVars; ++i) + { + DeclareConstBufferShaderVariable(glsl, + psCBuf->asVars[i].Name, + &psCBuf->asVars[i].sType, 0); + } + + bcatcstr(glsl, "};\n"); +} + +void DeclareBufferVariable(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, + ConstantBuffer* psCBuf, const Operand* psOperand, + const uint32_t ui32GloballyCoherentAccess, + const ResourceType eResourceType, + bstring glsl) +{ + bstring StructName; +#if defined(_DEBUG) + uint32_t unnamed_struct = +#endif + strcmp(psCBuf->asVars[0].Name, "$Element") == 0 ? 1 : 0; + + ASSERT(psCBuf->ui32NumVars == 1); + ASSERT(unnamed_struct); + + StructName = bfromcstr(""); + + //TranslateOperand(psContext, psOperand, TO_FLAG_NAME_ONLY); + if(psOperand->eType == OPERAND_TYPE_RESOURCE && eResourceType == RTYPE_STRUCTURED) + { + bformata(StructName, "StructuredRes%d", psOperand->ui32RegisterNumber); + } + else if(psOperand->eType == OPERAND_TYPE_RESOURCE && eResourceType == RTYPE_UAV_RWBYTEADDRESS) + { + bformata(StructName, "RawRes%d", psOperand->ui32RegisterNumber); + } + else + { + ResourceName(StructName, psContext, RGROUP_UAV, psOperand->ui32RegisterNumber, 0); + } + + PreDeclareStructType(glsl, + bstr2cstr(StructName, '\0'), + &psCBuf->asVars[0].sType); + + /* [layout (location = X)] uniform vec4 HLSLConstantBufferName[numConsts]; */ + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + bformata(glsl, "layout(binding = %d) ", ui32BindingPoint); + + if(ui32GloballyCoherentAccess & GLOBALLY_COHERENT_ACCESS) + { + bcatcstr(glsl, "coherent "); + } + + if(eResourceType == RTYPE_STRUCTURED) + { + bcatcstr(glsl, "readonly "); + } + + bformata(glsl, "buffer Block%d {\n", psOperand->ui32RegisterNumber); + + DeclareConstBufferShaderVariable(glsl, + bstr2cstr(StructName, '\0'), + &psCBuf->asVars[0].sType, + 1); + + bcatcstr(glsl, "};\n"); + + bdestroy(StructName); +} + + +void DeclareStructConstants(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, + ConstantBuffer* psCBuf, const Operand* psOperand, + bstring glsl) +{ + uint32_t i; + int useGlobalsStruct = 1; + + if(psContext->flags & HLSLCC_FLAG_DISABLE_GLOBALS_STRUCT && psCBuf->Name[0] == '$') + useGlobalsStruct = 0; + + if(useGlobalsStruct) + { + for(i=0; i < psCBuf->ui32NumVars; ++i) + { + PreDeclareStructType(glsl, + psCBuf->asVars[i].Name, + &psCBuf->asVars[i].sType); + } + } + + /* [layout (location = X)] uniform vec4 HLSLConstantBufferName[numConsts]; */ + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + bformata(glsl, "layout(location = %d) ", ui32BindingPoint); + if(useGlobalsStruct) + { + bcatcstr(glsl, "uniform struct "); + TranslateOperand(psContext, psOperand, TO_FLAG_DECLARATION_NAME); + + bcatcstr(glsl, "_Type {\n"); + } + + for(i=0; i < psCBuf->ui32NumVars; ++i) + { + if(!useGlobalsStruct) + bcatcstr(glsl, "uniform "); + + DeclareConstBufferShaderVariable(glsl, + psCBuf->asVars[i].Name, + &psCBuf->asVars[i].sType, 0); + } + + if(useGlobalsStruct) + { + bcatcstr(glsl, "} "); + + TranslateOperand(psContext, psOperand, TO_FLAG_DECLARATION_NAME); + + bcatcstr(glsl, ";\n"); +} +} + +char* GetSamplerType(HLSLCrossCompilerContext* psContext, + const RESOURCE_DIMENSION eDimension, + const uint32_t ui32RegisterNumber) +{ + ResourceBinding* psBinding = 0; + RESOURCE_RETURN_TYPE eType = RETURN_TYPE_UNORM; + int found; + found = GetResourceFromBindingPoint(RGROUP_TEXTURE, ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + if(found) + { + eType = (RESOURCE_RETURN_TYPE)psBinding->ui32ReturnType; + } + switch(eDimension) + { + case RESOURCE_DIMENSION_BUFFER: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isamplerBuffer"; + case RETURN_TYPE_UINT: + return "usamplerBuffer"; + default: + return "samplerBuffer"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE1D: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler1D"; + case RETURN_TYPE_UINT: + return "usampler1D"; + default: + return "sampler1D"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2D: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler2D"; + case RETURN_TYPE_UINT: + return "usampler2D"; + default: + return "sampler2D"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2DMS: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler2DMS"; + case RETURN_TYPE_UINT: + return "usampler2DMS"; + default: + return "sampler2DMS"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE3D: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler3D"; + case RETURN_TYPE_UINT: + return "usampler3D"; + default: + return "sampler3D"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURECUBE: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isamplerCube"; + case RETURN_TYPE_UINT: + return "usamplerCube"; + default: + return "samplerCube"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler1DArray"; + case RETURN_TYPE_UINT: + return "usampler1DArray"; + default: + return "sampler1DArray"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler2DArray"; + case RETURN_TYPE_UINT: + return "usampler2DArray"; + default: + return "sampler2DArray"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isampler2DMSArray"; + case RETURN_TYPE_UINT: + return "usampler2DMSArray"; + default: + return "sampler2DMSArray"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + switch(eType) + { + case RETURN_TYPE_SINT: + return "isamplerCubeArray"; + case RETURN_TYPE_UINT: + return "usamplerCubeArray"; + default: + return "samplerCubeArray"; + } + break; + } + } + + return "sampler2D"; +} + +static void TranslateResourceTexture(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, uint32_t samplerCanDoShadowCmp) +{ + bstring glsl = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + uint32_t i; + + const char* samplerTypeName = GetSamplerType(psContext, + psDecl->value.eResourceDimension, + psDecl->asOperands[0].ui32RegisterNumber); + + if (psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) + { + if(samplerCanDoShadowCmp && psDecl->ui32IsShadowTex) + { + for (i = 0; i < psDecl->ui32SamplerUsedCount; i++) + { + bcatcstr(glsl, "uniform "); + bcatcstr(glsl, samplerTypeName); + bcatcstr(glsl, "Shadow "); + ConcatTextureSamplerName(glsl, &psShader->sInfo, psDecl->asOperands[0].ui32RegisterNumber, psDecl->ui32SamplerUsed[i], 1); + bcatcstr(glsl, ";\n"); + } + } + for (i = 0; i < psDecl->ui32SamplerUsedCount; i++) + { + bcatcstr(glsl, "uniform "); + bcatcstr(glsl, samplerTypeName); + bcatcstr(glsl, " "); + ConcatTextureSamplerName(glsl, &psShader->sInfo, psDecl->asOperands[0].ui32RegisterNumber, psDecl->ui32SamplerUsed[i], 0); + bcatcstr(glsl, ";\n"); + } + } + + if(samplerCanDoShadowCmp && psDecl->ui32IsShadowTex) + { + //Create shadow and non-shadow sampler. + //HLSL does not have separate types for depth compare, just different functions. + + bcatcstr(glsl, "uniform "); + bcatcstr(glsl, samplerTypeName); + bcatcstr(glsl, "Shadow "); + ResourceName(glsl, psContext, RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, 1); + bcatcstr(glsl, ";\n"); + } + + bcatcstr(glsl, "uniform "); + bcatcstr(glsl, samplerTypeName); + bcatcstr(glsl, " "); + ResourceName(glsl, psContext, RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, 0); + bcatcstr(glsl, ";\n"); +} + +void TranslateDeclaration(HLSLCrossCompilerContext* psContext, const Declaration* psDecl) +{ + bstring glsl = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + switch(psDecl->eOpcode) + { + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_PS_SGV: + { + const SPECIAL_NAME eSpecialName = psDecl->asOperands[0].eSpecialName; + switch(eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinInput(psContext, psDecl, "gl_Position"); + break; + } + case NAME_RENDER_TARGET_ARRAY_INDEX: + { + AddBuiltinInput(psContext, psDecl, "gl_Layer"); + break; + } + case NAME_CLIP_DISTANCE: + { + AddBuiltinInput(psContext, psDecl, "gl_ClipDistance"); + break; + } + case NAME_VIEWPORT_ARRAY_INDEX: + { + AddBuiltinInput(psContext, psDecl, "gl_ViewportIndex"); + break; + } + case NAME_INSTANCE_ID: + { + AddBuiltinInput(psContext, psDecl, "gl_InstanceID"); + break; + } + case NAME_IS_FRONT_FACE: + { + /* + Cast to int used because + if(gl_FrontFacing != 0) failed to compiled on Intel HD 4000. + Suggests no implicit conversion for bool<->int. + */ + + AddBuiltinInput(psContext, psDecl, "int(gl_FrontFacing)"); + break; + } + case NAME_SAMPLE_INDEX: + { + AddBuiltinInput(psContext, psDecl, "gl_SampleID"); + break; + } + case NAME_VERTEX_ID: + { + AddBuiltinInput(psContext, psDecl, "gl_VertexID"); + break; + } + case NAME_PRIMITIVE_ID: + { + AddBuiltinInput(psContext, psDecl, "gl_PrimitiveID"); + break; + } + default: + { + bformata(glsl, "in vec4 %s;\n", psDecl->asOperands[0].pszSpecialName); + + bcatcstr(glsl, "#define "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, " %s\n", psDecl->asOperands[0].pszSpecialName); + break; + } + } + break; + } + + case OPCODE_DCL_OUTPUT_SIV: + { + switch(psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT4, 0, "gl_Position"); + break; + } + case NAME_RENDER_TARGET_ARRAY_INDEX: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_INT, 0, "gl_Layer"); + break; + } + case NAME_CLIP_DISTANCE: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_ClipDistance"); + break; + } + case NAME_VIEWPORT_ARRAY_INDEX: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_INT, 0, "gl_ViewportIndex"); + break; + } + case NAME_VERTEX_ID: + { + ASSERT(0); //VertexID is not an output + break; + } + case NAME_PRIMITIVE_ID: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_INT, 0, "gl_PrimitiveID"); + break; + } + case NAME_INSTANCE_ID: + { + ASSERT(0); //InstanceID is not an output + break; + } + case NAME_IS_FRONT_FACE: + { + ASSERT(0); //FrontFacing is not an output + break; + } + case NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR: + { + if(psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 4, "gl_TessLevelOuter"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[0]"); + } + break; + } + case NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[1]"); + break; + } + case NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[2]"); + break; + } + case NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[3]"); + break; + } + case NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR: + { + if(psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 3,"gl_TessLevelOuter"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[0]"); + } + break; + } + case NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[1]"); + break; + } + case NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[2]"); + break; + } + case NAME_FINAL_LINE_DENSITY_TESSFACTOR: + { + if(psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 2, "gl_TessLevelOuter"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[0]"); + } + break; + } + case NAME_FINAL_LINE_DETAIL_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelOuter[1]"); + break; + } + case NAME_FINAL_TRI_INSIDE_TESSFACTOR: + case NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR: + { + if(psContext->psShader->aIndexedOutput[psDecl->asOperands[0].ui32RegisterNumber]) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 2, "gl_TessLevelInner"); + } + else + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelInner[0]"); + } + break; + } + case NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR: + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT, 0, "gl_TessLevelInner[1]"); + break; + } + default: + { + bformata(glsl, "out vec4 %s;\n", psDecl->asOperands[0].pszSpecialName); + + bcatcstr(glsl, "#define "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, " %s\n", psDecl->asOperands[0].pszSpecialName); + break; + } + } + break; + } + case OPCODE_DCL_INPUT: + { + const Operand* psOperand = &psDecl->asOperands[0]; + //Force the number of components to be 4. +/*dcl_output o3.xy + dcl_output o3.z + +Would generate a vec2 and a vec3. We discard the second one making .z invalid! + +*/ + int iNumComponents = 4;//GetMaxComponentFromComponentMask(psOperand); + const char* StorageQualifier = "attribute"; + const char* InputName; + const char* Precision = ""; + + if((psOperand->eType == OPERAND_TYPE_INPUT_DOMAIN_POINT)|| + (psOperand->eType == OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID)|| + (psOperand->eType == OPERAND_TYPE_INPUT_COVERAGE_MASK)|| + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID)|| + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_GROUP_ID)|| + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP)|| + (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED) || + (psOperand->eType == OPERAND_TYPE_INPUT_FORK_INSTANCE_ID)) + { + break; + } + + //Already declared as part of an array. + if(psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + { + break; + } + + InputName = GetDeclaredInputName(psContext, psShader->eShaderType, psOperand); + + if(InOutSupported(psContext->psShader->eTargetLanguage)) + { + StorageQualifier = "in"; + } + + if(HavePrecisionQualifers(psShader->eTargetLanguage)) + { + switch(psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + { + Precision = "highp"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_2_8: + { + Precision = "lowp"; + break; + } + case OPERAND_MIN_PRECISION_SINT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_UINT_16: + { + Precision = "mediump"; + break; + } + } + } + + DeclareInput(psContext, psDecl, + "", StorageQualifier, Precision, iNumComponents, (OPERAND_INDEX_DIMENSION)psOperand->iIndexDims, InputName); + + break; + } + case OPCODE_DCL_INPUT_PS_SIV: + { + switch(psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinInput(psContext, psDecl, "gl_FragCoord"); + break; + } + } + break; + } + case OPCODE_DCL_INPUT_SIV: + { + break; + } + case OPCODE_DCL_INPUT_PS: + { + const Operand* psOperand = &psDecl->asOperands[0]; + int iNumComponents = 4;//GetMaxComponentFromComponentMask(psOperand); + const char* StorageQualifier = "varying"; + const char* Precision = ""; + const char* InputName = GetDeclaredInputName(psContext, PIXEL_SHADER, psOperand); + const char* Interpolation = ""; + + if(InOutSupported(psContext->psShader->eTargetLanguage)) + { + StorageQualifier = "in"; + } + + switch(psDecl->value.eInterpolation) + { + case INTERPOLATION_CONSTANT: + { + Interpolation = "flat"; + break; + } + case INTERPOLATION_LINEAR: + { + break; + } + case INTERPOLATION_LINEAR_CENTROID: + { + Interpolation = "centroid"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE: + { + Interpolation = "noperspective"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID: + { + Interpolation = "noperspective centroid"; + break; + } + case INTERPOLATION_LINEAR_SAMPLE: + { + Interpolation = "sample"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE: + { + Interpolation = "noperspective sample"; + break; + } + } + + if(HavePrecisionQualifers(psShader->eTargetLanguage)) + { + switch(psOperand->eMinPrecision) + { + case OPERAND_MIN_PRECISION_DEFAULT: + { + Precision = "highp"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_FLOAT_2_8: + { + Precision = "lowp"; + break; + } + case OPERAND_MIN_PRECISION_SINT_16: + { + Precision = "mediump"; + break; + } + case OPERAND_MIN_PRECISION_UINT_16: + { + Precision = "mediump"; + break; + } + } + } + + DeclareInput(psContext, psDecl, + Interpolation, StorageQualifier, Precision, iNumComponents, INDEX_1D, InputName); + + break; + } + case OPCODE_DCL_TEMPS: + { + const uint32_t ui32NumTemps = psDecl->value.ui32NumTemps; + + if(ui32NumTemps > 0) + { + bformata(glsl, "vec4 Temp[%d];\n", ui32NumTemps); + + bformata(glsl, "ivec4 Temp_int[%d];\n", ui32NumTemps); + if(HaveUVec(psShader->eTargetLanguage)) + { + bformata(glsl, "uvec4 Temp_uint[%d];\n", ui32NumTemps); + } + if(psShader->fp64) + { + bformata(glsl, "dvec4 Temp_double[%d];\n", ui32NumTemps); + } + } + + break; + } + case OPCODE_SPECIAL_DCL_IMMCONST: + { + const Operand* psDest = &psDecl->asOperands[0]; + const Operand* psSrc = &psDecl->asOperands[1]; + + ASSERT(psSrc->eType == OPERAND_TYPE_IMMEDIATE32); + if(psDest->eType == OPERAND_TYPE_SPECIAL_IMMCONSTINT) + { + bformata(glsl, "const ivec4 IntImmConst%d = ", psDest->ui32RegisterNumber); + } + else + { + bformata(glsl, "const vec4 ImmConst%d = ", psDest->ui32RegisterNumber); + AddToDx9ImmConstIndexableArray(psContext, psDest); + } + TranslateOperand(psContext, psSrc, psDest->eType == OPERAND_TYPE_SPECIAL_IMMCONSTINT ? TO_FLAG_INTEGER : TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(glsl, ";\n"); + + break; + } + case OPCODE_DCL_CONSTANT_BUFFER: + { + const Operand* psOperand = &psDecl->asOperands[0]; + const uint32_t ui32BindingPoint = psOperand->aui32ArraySizes[0]; + + const char* StageName = "VS"; + + switch(psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + StageName = "PS"; + break; + } + case HULL_SHADER: + { + StageName = "HS"; + break; + } + case DOMAIN_SHADER: + { + StageName = "DS"; + break; + } + case GEOMETRY_SHADER: + { + StageName = "GS"; + break; + } + case COMPUTE_SHADER: + { + StageName = "CS"; + break; + } + default: + { + break; + } + } + + ConstantBuffer* psCBuf = NULL; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, ui32BindingPoint, &psContext->psShader->sInfo, &psCBuf); + + if (psCBuf) + { + // Constant buffers declared as "dynamicIndexed" are declared as raw vec4 arrays, as there is no general way to retrieve the member corresponding to a dynamic index. + // Simple cases can probably be handled easily, but for example when arrays (possibly nested with structs) are contained in the constant buffer and the shader reads + // from a dynamic index we would need to "undo" the operations done in order to compute the variable offset, and such a feature is not available at the moment. + psCBuf->blob = psDecl->value.eCBAccessPattern == CONSTANT_BUFFER_ACCESS_PATTERN_DYNAMICINDEXED; + } + + // We don't have a original resource name, maybe generate one??? + if(!psCBuf) + { + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + bformata(glsl, "layout(location = %d) ",ui32BindingPoint); + + bformata(glsl, "layout(std140) uniform ConstantBuffer%d {\n\tvec4 data[%d];\n} cb%d;\n", ui32BindingPoint,psOperand->aui32ArraySizes[1],ui32BindingPoint); + break; + } + else if (psCBuf->blob) + { + bformata(glsl, "layout(std140) uniform %s%s {\n\tvec4 %s%s_data[%d];\n};\n", psCBuf->Name, StageName, psCBuf->Name, StageName, psOperand->aui32ArraySizes[1]); + break; + } + + if(psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) + { + if(psContext->flags & HLSLCC_FLAG_GLOBAL_CONSTS_NEVER_IN_UBO && psCBuf->Name[0] == '$') + { + DeclareStructConstants(psContext, ui32BindingPoint, psCBuf, psOperand, glsl); + } + else + { + DeclareUBOConstants(psContext, ui32BindingPoint, psCBuf, glsl); + } + } + else + { + DeclareStructConstants(psContext, ui32BindingPoint, psCBuf, psOperand, glsl); + } + break; + } + case OPCODE_DCL_RESOURCE: + { + if (HaveUniformBindingsAndLocations(psContext->psShader->eTargetLanguage, psContext->psShader->extensions, psContext->flags)) + { + // Explicit layout bindings are not currently compatible with combined texture samplers. The layout below assumes there is exactly one GLSL sampler + // for each HLSL texture declaration, but when combining textures+samplers, there can be multiple OGL samplers for each HLSL texture declaration. + if((psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) != HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) + { + //Constant buffer locations start at 0. Resource locations start at ui32NumConstantBuffers. + bformata(glsl, "layout(location = %d) ", + psContext->psShader->sInfo.ui32NumConstantBuffers + psDecl->asOperands[0].ui32RegisterNumber); + } + } + + switch(psDecl->value.eResourceDimension) + { + case RESOURCE_DIMENSION_BUFFER: + { + bformata(glsl, "uniform %s ", GetSamplerType(psContext, + RESOURCE_DIMENSION_BUFFER, + psDecl->asOperands[0].ui32RegisterNumber)); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bcatcstr(glsl, ";\n"); + break; + } + case RESOURCE_DIMENSION_TEXTURE1D: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMS: + { + TranslateResourceTexture(psContext, psDecl, 0); + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + TranslateResourceTexture(psContext, psDecl, 0); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + TranslateResourceTexture(psContext, psDecl, 0); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + } + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_TEXTURES); + psShader->aeResourceDims[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.eResourceDimension; + break; + } + case OPCODE_DCL_OUTPUT: + { + if(psShader->eShaderType == HULL_SHADER && psDecl->asOperands[0].ui32RegisterNumber==0) + { + AddBuiltinOutput(psContext, psDecl, GLVARTYPE_FLOAT4, 0, "gl_out[gl_InvocationID].gl_Position"); + } + else + { + AddUserOutput(psContext, psDecl); + } + break; + } + case OPCODE_DCL_GLOBAL_FLAGS: + { + uint32_t ui32Flags = psDecl->value.ui32GlobalFlags; + + if(ui32Flags & GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL) + { + bcatcstr(glsl, "layout(early_fragment_tests) in;\n"); + } + if(!(ui32Flags & GLOBAL_FLAG_REFACTORING_ALLOWED)) + { + //TODO add precise + //HLSL precise - http://msdn.microsoft.com/en-us/library/windows/desktop/hh447204(v=vs.85).aspx + } + if(ui32Flags & GLOBAL_FLAG_ENABLE_DOUBLE_PRECISION_FLOAT_OPS) + { + bcatcstr(glsl, "#extension GL_ARB_gpu_shader_fp64 : enable\n"); + psShader->fp64 = 1; + } + break; + } + + case OPCODE_DCL_THREAD_GROUP: + { + bformata(glsl, "layout(local_size_x = %d, local_size_y = %d, local_size_z = %d) in;\n", + psDecl->value.aui32WorkGroupSize[0], + psDecl->value.aui32WorkGroupSize[1], + psDecl->value.aui32WorkGroupSize[2]); + break; + } + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + { + if(psContext->psShader->eShaderType == HULL_SHADER) + { + psContext->psShader->sInfo.eTessOutPrim = psDecl->value.eTessOutPrim; + } + break; + } + case OPCODE_DCL_TESS_DOMAIN: + { + if(psContext->psShader->eShaderType == DOMAIN_SHADER) + { + switch(psDecl->value.eTessDomain) + { + case TESSELLATOR_DOMAIN_ISOLINE: + { + bcatcstr(glsl, "layout(isolines) in;\n"); + break; + } + case TESSELLATOR_DOMAIN_TRI: + { + bcatcstr(glsl, "layout(triangles) in;\n"); + break; + } + case TESSELLATOR_DOMAIN_QUAD: + { + bcatcstr(glsl, "layout(quads) in;\n"); + break; + } + default: + { + break; + } + } + } + break; + } + case OPCODE_DCL_TESS_PARTITIONING: + { + if(psContext->psShader->eShaderType == HULL_SHADER) + { + psContext->psShader->sInfo.eTessPartitioning = psDecl->value.eTessPartitioning; + } + break; + } + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + { + switch(psDecl->value.eOutputPrimitiveTopology) + { + case PRIMITIVE_TOPOLOGY_POINTLIST: + { + bcatcstr(glsl, "layout(points) out;\n"); + break; + } + case PRIMITIVE_TOPOLOGY_LINELIST_ADJ: + case PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ: + case PRIMITIVE_TOPOLOGY_LINELIST: + case PRIMITIVE_TOPOLOGY_LINESTRIP: + { + bcatcstr(glsl, "layout(line_strip) out;\n"); + break; + } + + case PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ: + case PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ: + case PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: + case PRIMITIVE_TOPOLOGY_TRIANGLELIST: + { + bcatcstr(glsl, "layout(triangle_strip) out;\n"); + break; + } + default: + { + break; + } + } + break; + } + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + { + bformata(glsl, "layout(max_vertices = %d) out;\n", psDecl->value.ui32MaxOutputVertexCount); + break; + } + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + { + switch(psDecl->value.eInputPrimitive) + { + case PRIMITIVE_POINT: + { + bcatcstr(glsl, "layout(points) in;\n"); + break; + } + case PRIMITIVE_LINE: + { + bcatcstr(glsl, "layout(lines) in;\n"); + break; + } + case PRIMITIVE_LINE_ADJ: + { + bcatcstr(glsl, "layout(lines_adjacency) in;\n"); + break; + } + case PRIMITIVE_TRIANGLE: + { + bcatcstr(glsl, "layout(triangles) in;\n"); + break; + } + case PRIMITIVE_TRIANGLE_ADJ: + { + bcatcstr(glsl, "layout(triangles_adjacency) in;\n"); + break; + } + default: + { + break; + } + } + break; + } + case OPCODE_DCL_INTERFACE: + { + const uint32_t interfaceID = psDecl->value.interface.ui32InterfaceID; + const uint32_t numUniforms = psDecl->value.interface.ui32ArraySize; + const uint32_t ui32NumBodiesPerTable = psContext->psShader->funcPointer[interfaceID].ui32NumBodiesPerTable; + ShaderVar* psVar; + uint32_t varFound; + + const char* uniformName; + + varFound = GetInterfaceVarFromOffset(interfaceID, &psContext->psShader->sInfo, &psVar); + ASSERT(varFound); + uniformName = &psVar->Name[0]; + + bformata(glsl, "subroutine uniform SubroutineType %s[%d*%d];\n", uniformName, numUniforms, ui32NumBodiesPerTable); + break; + } + case OPCODE_DCL_FUNCTION_BODY: + { + //bformata(glsl, "void Func%d();//%d\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].eType); + break; + } + case OPCODE_DCL_FUNCTION_TABLE: + { + break; + } + case OPCODE_CUSTOMDATA: + { + const uint32_t ui32NumVec4 = psDecl->ui32NumOperands; + const uint32_t ui32NumVec4Minus1 = (ui32NumVec4-1); + uint32_t ui32ConstIndex = 0; + float x, y, z, w; + + //If ShaderBitEncodingSupported then 1 integer buffer, use intBitsToFloat to get float values. - More instructions. + //else 2 buffers - one integer and one float. - More data + + if(ShaderBitEncodingSupported(psShader->eTargetLanguage) == 0) + { + bcatcstr(glsl, "#define immediateConstBufferI(idx) immediateConstBufferInt[idx]\n"); + bcatcstr(glsl, "#define immediateConstBufferF(idx) immediateConstBuffer[idx]\n"); + + bformata(glsl, "vec4 immediateConstBuffer[%d] = vec4[%d] (\n", ui32NumVec4, ui32NumVec4); + for(;ui32ConstIndex < ui32NumVec4Minus1; ui32ConstIndex++) + { + float loopLocalX, loopLocalY, loopLocalZ, loopLocalW; + loopLocalX = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + loopLocalY = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + loopLocalZ = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + loopLocalW = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + + //A single vec4 can mix integer and float types. + //Forced NAN and INF to zero inside the immediate constant buffer. This will allow the shader to compile. + if(fpcheck(loopLocalX)) + { + loopLocalX = 0; + } + if(fpcheck(loopLocalY)) + { + loopLocalY = 0; + } + if(fpcheck(loopLocalZ)) + { + loopLocalZ = 0; + } + if(fpcheck(loopLocalW)) + { + loopLocalW = 0; + } + + bformata(glsl, "\tvec4(%f, %f, %f, %f), \n", loopLocalX, loopLocalY, loopLocalZ, loopLocalW); + } + //No trailing comma on this one + x = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + y = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + z = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + w = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + if(fpcheck(x)) + { + x = 0; + } + if(fpcheck(y)) + { + y = 0; + } + if(fpcheck(z)) + { + z = 0; + } + if(fpcheck(w)) + { + w = 0; + } + bformata(glsl, "\tvec4(%f, %f, %f, %f)\n", x, y, z, w); + bcatcstr(glsl, ");\n"); + } + else + { + bcatcstr(glsl, "#define immediateConstBufferI(idx) immediateConstBufferInt[idx]\n"); + bcatcstr(glsl, "#define immediateConstBufferF(idx) intBitsToFloat(immediateConstBufferInt[idx])\n"); + } + + { + uint32_t ui32ConstIndex2 = 0; + int x2, y2, z2, w2; + + bformata(glsl, "ivec4 immediateConstBufferInt[%d] = ivec4[%d] (\n", ui32NumVec4, ui32NumVec4); + for (; ui32ConstIndex2 < ui32NumVec4Minus1; ui32ConstIndex2++) + { + int loopLocalX, loopLocalY, loopLocalZ, loopLocalW; + loopLocalX = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].a; + loopLocalY = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].b; + loopLocalZ = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].c; + loopLocalW = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].d; + + bformata(glsl, "\tivec4(%d, %d, %d, %d), \n", loopLocalX, loopLocalY, loopLocalZ, loopLocalW); + } + //No trailing comma on this one + x2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].a; + y2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].b; + z2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].c; + w2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].d; + + bformata(glsl, "\tivec4(%d, %d, %d, %d)\n", x2, y2, z2, w2); + bcatcstr(glsl, ");\n"); + } + + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + const uint32_t forkPhaseNum = psDecl->value.aui32HullPhaseInstanceInfo[0]; + const uint32_t instanceCount = psDecl->value.aui32HullPhaseInstanceInfo[1]; + bformata(glsl, "const int HullPhase%dInstanceCount = %d;\n", forkPhaseNum, instanceCount); + break; + } + case OPCODE_DCL_INDEXABLE_TEMP: + { + const uint32_t ui32RegIndex = psDecl->sIdxTemp.ui32RegIndex; + const uint32_t ui32RegCount = psDecl->sIdxTemp.ui32RegCount; + const uint32_t ui32RegComponentSize = psDecl->sIdxTemp.ui32RegComponentSize; + bformata(glsl, "vec%d TempArray%d[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + bformata(glsl, "ivec%d TempArray%d_int[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + if(HaveUVec(psShader->eTargetLanguage)) + { + bformata(glsl, "uvec%d TempArray%d_uint[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + } + if(psShader->fp64) + { + bformata(glsl, "dvec%d TempArray%d_double[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + } + break; + } + case OPCODE_DCL_INDEX_RANGE: + { + break; + } + case OPCODE_HS_DECLS: + { + break; + } + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + { + break; + } + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + { + if(psContext->psShader->eShaderType == HULL_SHADER) + { + bformata(glsl, "layout(vertices=%d) out;\n", psDecl->value.ui32MaxOutputVertexCount); + } + break; + } + case OPCODE_HS_FORK_PHASE: + { + break; + } + case OPCODE_HS_JOIN_PHASE: + { + break; + } + case OPCODE_DCL_SAMPLER: + { + break; + } + case OPCODE_DCL_HS_MAX_TESSFACTOR: + { + //For GLSL the max tessellation factor is fixed to the value of gl_MaxTessGenLevel. + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + { + // non-float images need either 'i' or 'u' prefix. + char imageTypePrefix[2] = { 0, 0 }; + if(psDecl->sUAV.ui32GloballyCoherentAccess & GLOBALLY_COHERENT_ACCESS) + { + bcatcstr(glsl, "coherent "); + } + + if(psShader->aiOpcodeUsed[OPCODE_LD_UAV_TYPED] == 0) + { + bcatcstr(glsl, "writeonly "); + } + else + { + if(psShader->aiOpcodeUsed[OPCODE_STORE_UAV_TYPED] == 0) + { + bcatcstr(glsl, "readonly "); + } + + switch(psDecl->sUAV.Type) + { + case RETURN_TYPE_FLOAT: + bcatcstr(glsl, "layout(rgba32f) "); + break; + case RETURN_TYPE_UNORM: + bcatcstr(glsl, "layout(rgba8) "); + break; + case RETURN_TYPE_SNORM: + bcatcstr(glsl, "layout(rgba8_snorm) "); + break; + case RETURN_TYPE_UINT: + bcatcstr(glsl, "layout(rgba32ui) "); + imageTypePrefix[0] = 'u'; + break; + case RETURN_TYPE_SINT: + bcatcstr(glsl, "layout(rgba32i) "); + imageTypePrefix[0] = 'i'; + break; + default: + ASSERT(0); + } + } + + switch(psDecl->value.eResourceDimension) + { + case RESOURCE_DIMENSION_BUFFER: + { + bformata(glsl, "uniform %simageBuffer ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE1D: + { + bformata(glsl, "uniform %simage1D ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + bformata(glsl, "uniform %simage2D ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMS: + { + bformata(glsl, "uniform %simage2DMS ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + bformata(glsl, "uniform %simage3D ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + bformata(glsl, "uniform %simageCube ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + bformata(glsl, "uniform %simage1DArray ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + bformata(glsl, "uniform %simage2DArray ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + bformata(glsl, "uniform %simage3DArray ", imageTypePrefix); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + bformata(glsl, "uniform %simageCubeArray ", imageTypePrefix); + break; + } + } + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + { + const uint32_t ui32BindingPoint = psDecl->asOperands[0].aui32ArraySizes[0]; + ConstantBuffer* psCBuf = NULL; + + if(psDecl->sUAV.bCounter) + { + bformata(glsl, "layout (binding = 1) uniform atomic_uint "); + ResourceName(glsl, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bformata(glsl, "_counter; \n"); + } + + GetConstantBufferFromBindingPoint(RGROUP_UAV, ui32BindingPoint, &psContext->psShader->sInfo, &psCBuf); + + DeclareBufferVariable(psContext, ui32BindingPoint, psCBuf, &psDecl->asOperands[0], + psDecl->sUAV.ui32GloballyCoherentAccess, RTYPE_UAV_RWSTRUCTURED, glsl); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + { + if(psDecl->sUAV.bCounter) + { + bformata(glsl, "layout (binding = 1) uniform atomic_uint "); + ResourceName(glsl, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bformata(glsl, "_counter; \n"); + } + + bformata(glsl, "buffer Block%d {\n\tuint ", psDecl->asOperands[0].ui32RegisterNumber); + ResourceName(glsl, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bcatcstr(glsl, "[];\n};\n"); + + break; + } + case OPCODE_DCL_RESOURCE_STRUCTURED: + { + ConstantBuffer* psCBuf = NULL; + + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + DeclareBufferVariable(psContext, psDecl->asOperands[0].ui32RegisterNumber, psCBuf, &psDecl->asOperands[0], + 0, RTYPE_STRUCTURED, glsl); + break; + } + case OPCODE_DCL_RESOURCE_RAW: + { + bformata(glsl, "buffer Block%d {\n\tuint RawRes%d[];\n};\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber); + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + { + ShaderVarType* psVarType = &psShader->sGroupSharedVarType[psDecl->asOperands[0].ui32RegisterNumber]; + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_GROUPSHARED); + + bcatcstr(glsl, "shared struct {\n"); + bformata(glsl, "uint value[%d];\n", psDecl->sTGSM.ui32Stride/4); + bcatcstr(glsl, "} "); + TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(glsl, "[%d];\n", + psDecl->sTGSM.ui32Count); + + memset(psVarType, 0, sizeof(ShaderVarType)); + strcpy(psVarType->Name, "$Element"); + + psVarType->Columns = psDecl->sTGSM.ui32Stride/4; + psVarType->Elements = psDecl->sTGSM.ui32Count; + break; + } + case OPCODE_DCL_STREAM: + { + ASSERT(psDecl->asOperands[0].eType == OPERAND_TYPE_STREAM); + + psShader->ui32CurrentVertexOutputStream = psDecl->asOperands[0].ui32RegisterNumber; + + bformata(glsl, "layout(stream = %d) out;\n", psShader->ui32CurrentVertexOutputStream); + + break; + } + case OPCODE_DCL_GS_INSTANCE_COUNT: + { + bformata(glsl, "layout(invocations = %d) in;\n", psDecl->value.ui32GSInstanceCount); + break; + } + default: + { + ASSERT(0); + break; + } + } +} + +//Convert from per-phase temps to global temps for GLSL. +void ConsolidateHullTempVars(ShaderData* psShader) +{ + uint32_t i, k; + uint32_t ui32Phase, ui32Instance; + const uint32_t ui32NumDeclLists = psShader->asPhase[HS_FORK_PHASE].ui32InstanceCount + + psShader->asPhase[HS_CTRL_POINT_PHASE].ui32InstanceCount + + psShader->asPhase[HS_JOIN_PHASE].ui32InstanceCount + + psShader->asPhase[HS_GLOBAL_DECL].ui32InstanceCount; + + Declaration** pasDeclArray = hlslcc_malloc(sizeof(Declaration*) * ui32NumDeclLists); + + uint32_t* pui32DeclCounts = hlslcc_malloc(sizeof(uint32_t) * ui32NumDeclLists); + uint32_t ui32NumTemps = 0; + + i=0; + for(ui32Phase = HS_GLOBAL_DECL; ui32Phase < NUM_PHASES; ui32Phase++) + { + for(ui32Instance = 0; ui32Instance < psShader->asPhase[ui32Phase].ui32InstanceCount; ++ui32Instance) + { + pasDeclArray[i] = psShader->asPhase[ui32Phase].ppsDecl[ui32Instance]; + pui32DeclCounts[i++] = psShader->asPhase[ui32Phase].pui32DeclCount[ui32Instance]; + } + } + + for(k = 0; k < ui32NumDeclLists; ++k) + { + for(i=0; i < pui32DeclCounts[k]; ++i) + { + Declaration* psDecl = pasDeclArray[k]+i; + + if(psDecl->eOpcode == OPCODE_DCL_TEMPS) + { + if(ui32NumTemps < psDecl->value.ui32NumTemps) + { + //Find the total max number of temps needed by the entire + //shader. + ui32NumTemps = psDecl->value.ui32NumTemps; + } + //Only want one global temp declaration. + psDecl->value.ui32NumTemps = 0; + } + } + } + + //Find the first temp declaration and make it + //declare the max needed amount of temps. + for(k = 0; k < ui32NumDeclLists; ++k) + { + for(i=0; i < pui32DeclCounts[k]; ++i) + { + Declaration* psDecl = pasDeclArray[k]+i; + + if(psDecl->eOpcode == OPCODE_DCL_TEMPS) + { + psDecl->value.ui32NumTemps = ui32NumTemps; + return; + } + } + } + + hlslcc_free(pasDeclArray); + hlslcc_free(pui32DeclCounts); +} + diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLInstruction.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLInstruction.c new file mode 100644 index 0000000000..cb72838092 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLInstruction.c @@ -0,0 +1,4576 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toGLSLInstruction.h" +#include +#include "bstrlib.h" +#include "hlslcc.h" +#include "internal_includes/debug.h" +#include "internal_includes/languages.h" +#include "internal_includes/toGLSLOperand.h" +#include "stdio.h" + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); +static int GLSLIsIntegerImmediateOpcode(OPCODE_TYPE eOpcode); + +// Calculate the bits set in mask +static int GLSLWriteMaskToComponentCount(uint32_t writeMask) +{ + uint32_t count; + // In HLSL bytecode writemask 0 also means everything + if (writeMask == 0) + return 4; + + // Count bits set + // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64 + count = (writeMask * 0x200040008001ULL & 0x111111111111111ULL) % 0xf; + + return (int)count; +} + +static uint32_t GLSLBuildComponentMaskFromElementCount(int count) +{ + // Translate numComponents into bitmask + // 1 -> 1, 2 -> 3, 3 -> 7 and 4 -> 15 + return (1 << count) - 1; +} + +// This function prints out the destination name, possible destination writemask, assignment operator +// and any possible conversions needed based on the eSrcType+ui32SrcElementCount (type and size of data expected to be coming in) +// As an output, pNeedsParenthesis will be filled with the amount of closing parenthesis needed +// and pSrcCount will be filled with the number of components expected +// ui32CompMask can be used to only write to 1 or more components (used by MOVC) +static void GLSLAddOpAssignToDestWithMask(HLSLCrossCompilerContext* psContext, + const Operand* psDest, + SHADER_VARIABLE_TYPE eSrcType, + uint32_t ui32SrcElementCount, + const char* szAssignmentOp, + int* pNeedsParenthesis, + uint32_t ui32CompMask) +{ + uint32_t ui32DestElementCount = GetNumSwizzleElementsWithMask(psDest, ui32CompMask); + bstring glsl = *psContext->currentShaderString; + SHADER_VARIABLE_TYPE eDestDataType = GetOperandDataType(psContext, psDest); + ASSERT(pNeedsParenthesis != NULL); + + *pNeedsParenthesis = 0; + + TranslateOperandWithMask(psContext, psDest, TO_FLAG_DESTINATION, ui32CompMask); + + // Simple path: types match. + if (eDestDataType == eSrcType) + { + // Cover cases where the HLSL language expects the rest of the components to be default-filled + // eg. MOV r0, c0.x => Temp[0] = vec4(c0.x); + if (ui32DestElementCount > ui32SrcElementCount) + { + bformata(glsl, " %s %s(", szAssignmentOp, GetConstructorForType(eDestDataType, ui32DestElementCount)); + *pNeedsParenthesis = 1; + } + else + bformata(glsl, " %s ", szAssignmentOp); + return; + } + + switch (eDestDataType) + { + case SVT_INT: + if (eSrcType == SVT_FLOAT && psContext->psShader->ui32MajorVersion > 3) + { + bformata(glsl, " %s floatBitsToInt(", szAssignmentOp); + // Cover cases where the HLSL language expects the rest of the components to be default-filled + if (ui32DestElementCount > ui32SrcElementCount) + { + bformata(glsl, "%s(", GetConstructorForType(eSrcType, ui32DestElementCount)); + (*pNeedsParenthesis)++; + } + } + else + bformata(glsl, " %s %s(", szAssignmentOp, GetConstructorForType(eDestDataType, ui32DestElementCount)); + break; + case SVT_UINT: + if (eSrcType == SVT_FLOAT && psContext->psShader->ui32MajorVersion > 3) + { + bformata(glsl, " %s floatBitsToUint(", szAssignmentOp); + // Cover cases where the HLSL language expects the rest of the components to be default-filled + if (ui32DestElementCount > ui32SrcElementCount) + { + bformata(glsl, "%s(", GetConstructorForType(eSrcType, ui32DestElementCount)); + (*pNeedsParenthesis)++; + } + } + else + bformata(glsl, " %s %s(", szAssignmentOp, GetConstructorForType(eDestDataType, ui32DestElementCount)); + break; + + case SVT_FLOAT: + if (psContext->psShader->ui32MajorVersion > 3) + { + if (eSrcType == SVT_INT) + bformata(glsl, " %s intBitsToFloat(", szAssignmentOp); + else + bformata(glsl, " %s uintBitsToFloat(", szAssignmentOp); + // Cover cases where the HLSL language expects the rest of the components to be default-filled + if (ui32DestElementCount > ui32SrcElementCount) + { + bformata(glsl, "%s(", GetConstructorForType(eSrcType, ui32DestElementCount)); + (*pNeedsParenthesis)++; + } + } + else + bformata(glsl, " %s %s(", szAssignmentOp, GetConstructorForType(eDestDataType, ui32DestElementCount)); + break; + default: + // TODO: Handle bools? + break; + } + (*pNeedsParenthesis)++; + return; +} + +static void GLSLMETALAddAssignToDest(HLSLCrossCompilerContext* psContext, + const Operand* psDest, + SHADER_VARIABLE_TYPE eSrcType, + uint32_t ui32SrcElementCount, + int* pNeedsParenthesis) +{ + GLSLAddOpAssignToDestWithMask(psContext, psDest, eSrcType, ui32SrcElementCount, "=", pNeedsParenthesis, OPERAND_4_COMPONENT_MASK_ALL); +} + +static void GLSLAddAssignPrologue(HLSLCrossCompilerContext* psContext, int numParenthesis) +{ + bstring glsl = *psContext->currentShaderString; + while (numParenthesis != 0) + { + bcatcstr(glsl, ")"); + numParenthesis--; + } + bcatcstr(glsl, ";\n"); +} +static uint32_t GLSLResourceReturnTypeToFlag(const RESOURCE_RETURN_TYPE eType) +{ + if (eType == RETURN_TYPE_SINT) + { + return TO_FLAG_INTEGER; + } + else if (eType == RETURN_TYPE_UINT) + { + return TO_FLAG_UNSIGNED_INTEGER; + } + else + { + return TO_FLAG_NONE; + } +} + +typedef enum +{ + GLSL_CMP_EQ, + GLSL_CMP_LT, + GLSL_CMP_GE, + GLSL_CMP_NE, +} GLSLComparisonType; + +static void GLSLAddComparision(HLSLCrossCompilerContext* psContext, Instruction* psInst, GLSLComparisonType eType, uint32_t typeFlag, Instruction* psNextInst) +{ + // Multiple cases to consider here: + // For shader model <=3: all comparisons are floats + // otherwise: + // OPCODE_LT, _GT, _NE etc: inputs are floats, outputs UINT 0xffffffff or 0. typeflag: TO_FLAG_NONE + // OPCODE_ILT, _IGT etc: comparisons are signed ints, outputs UINT 0xffffffff or 0 typeflag TO_FLAG_INTEGER + // _ULT, UGT etc: inputs unsigned ints, outputs UINTs typeflag TO_FLAG_UNSIGNED_INTEGER + // + // Additional complexity: if dest swizzle element count is 1, we can use normal comparison operators, otherwise glsl intrinsics. + + bstring glsl = *psContext->currentShaderString; + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + const uint32_t s1ElemCount = GetNumSwizzleElements(&psInst->asOperands[2]); + + int floatResult = 0; + int needsParenthesis = 0; + + ASSERT(s0ElemCount == s1ElemCount || s1ElemCount == 1 || s0ElemCount == 1); + if (s0ElemCount != s1ElemCount) + { + // Set the proper auto-expand flag is either argument is scalar + typeFlag |= (TO_AUTO_EXPAND_TO_VEC2 << (max(s0ElemCount, s1ElemCount) - 2)); + } + + if (psContext->psShader->ui32MajorVersion < 4) + { + floatResult = 1; + } + + if (destElemCount > 1) + { + const char* glslOpcode[] = { + "equal", + "lessThan", + "greaterThanEqual", + "notEqual", + }; + + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], floatResult ? SVT_FLOAT : SVT_UINT, destElemCount, &needsParenthesis); + + bcatcstr(glsl, GetConstructorForType(floatResult ? SVT_FLOAT : SVT_UINT, destElemCount)); + bformata(glsl, "(%s(", glslOpcode[eType]); + TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + bcatcstr(glsl, "))"); + if (!floatResult) + { + bcatcstr(glsl, " * 0xFFFFFFFFu"); + } + + GLSLAddAssignPrologue(psContext, needsParenthesis); + } + else + { + const char* glslOpcode[] = { + "==", + "<", + ">=", + "!=", + }; + + // Scalar compare + + // Optimization shortcut for the IGE+BREAKC_NZ combo: + // First print out the if(cond)->break directly, and then + // to guarantee correctness with side-effects, re-run + // the actual comparison. In most cases, the second run will + // be removed by the shader compiler optimizer pass (dead code elimination) + // This also makes it easier for some GLSL optimizers to recognize the for loop. + + if (psInst->eOpcode == OPCODE_IGE && psNextInst && psNextInst->eOpcode == OPCODE_BREAKC && + (psInst->asOperands[0].ui32RegisterNumber == psNextInst->asOperands[0].ui32RegisterNumber)) + { + AddIndentation(psContext); + bcatcstr(glsl, "// IGE+BREAKC opt\n"); + AddIndentation(psContext); + + if (psNextInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO) + bcatcstr(glsl, "if (("); + else + bcatcstr(glsl, "if (!("); + TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + bformata(glsl, "%s ", glslOpcode[eType]); + TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + bcatcstr(glsl, ")) { break; }\n"); + + // Mark the BREAKC instruction as already handled + psNextInst->eOpcode = OPCODE_NOP; + + // Continue as usual + } + + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], floatResult ? SVT_FLOAT : SVT_UINT, destElemCount, &needsParenthesis); + + bcatcstr(glsl, "("); + TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + bformata(glsl, "%s", glslOpcode[eType]); + TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + if (floatResult) + { + bcatcstr(glsl, ") ? 1.0 : 0.0"); + } + else + { + bcatcstr(glsl, ") ? 0xFFFFFFFFu : 0u"); + } + GLSLAddAssignPrologue(psContext, needsParenthesis); + } +} + +static void GLSLAddMOVBinaryOp(HLSLCrossCompilerContext* psContext, const Operand* pDest, Operand* pSrc) +{ + int numParenthesis = 0; + int srcSwizzleCount = GetNumSwizzleElements(pSrc); + uint32_t writeMask = GetOperandWriteMask(pDest); + + const SHADER_VARIABLE_TYPE eSrcType = GetOperandDataTypeEx(psContext, pSrc, GetOperandDataType(psContext, pDest)); + uint32_t flags = SVTTypeToFlag(eSrcType); + + GLSLMETALAddAssignToDest(psContext, pDest, eSrcType, srcSwizzleCount, &numParenthesis); + TranslateOperandWithMask(psContext, pSrc, flags, writeMask); + + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static uint32_t GLSLElemCountToAutoExpandFlag(uint32_t elemCount) +{ + return TO_AUTO_EXPAND_TO_VEC2 << (elemCount - 2); +} + +static void GLSLAddMOVCBinaryOp(HLSLCrossCompilerContext* psContext, const Operand* pDest, const Operand* src0, Operand* src1, Operand* src2) +{ + bstring glsl = *psContext->currentShaderString; + uint32_t destElemCount = GetNumSwizzleElements(pDest); + uint32_t s0ElemCount = GetNumSwizzleElements(src0); + uint32_t s1ElemCount = GetNumSwizzleElements(src1); + uint32_t s2ElemCount = GetNumSwizzleElements(src2); + uint32_t destWriteMask = GetOperandWriteMask(pDest); + uint32_t destElem; + + const SHADER_VARIABLE_TYPE eDestType = GetOperandDataType(psContext, pDest); + /* + for each component in dest[.mask] + if the corresponding component in src0 (POS-swizzle) + has any bit set + { + copy this component (POS-swizzle) from src1 into dest + } + else + { + copy this component (POS-swizzle) from src2 into dest + } + endfor + */ + + /* Single-component conditional variable (src0) */ + if (s0ElemCount == 1 || IsSwizzleReplicated(src0)) + { + int numParenthesis = 0; + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, pDest, eDestType, destElemCount, &numParenthesis); + bcatcstr(glsl, "("); + TranslateOperand(psContext, src0, TO_AUTO_BITCAST_TO_INT); + if (s0ElemCount > 1) + bcatcstr(glsl, ".x"); + if (psContext->psShader->ui32MajorVersion < 4) + { + // cmp opcode uses >= 0 + bcatcstr(glsl, " >= 0) ? "); + } + else + { + bcatcstr(glsl, " != 0) ? "); + } + + if (s1ElemCount == 1 && destElemCount > 1) + TranslateOperand(psContext, src1, SVTTypeToFlag(eDestType) | GLSLElemCountToAutoExpandFlag(destElemCount)); + else + TranslateOperandWithMask(psContext, src1, SVTTypeToFlag(eDestType), destWriteMask); + + bcatcstr(glsl, " : "); + if (s2ElemCount == 1 && destElemCount > 1) + TranslateOperand(psContext, src2, SVTTypeToFlag(eDestType) | GLSLElemCountToAutoExpandFlag(destElemCount)); + else + TranslateOperandWithMask(psContext, src2, SVTTypeToFlag(eDestType), destWriteMask); + + GLSLAddAssignPrologue(psContext, numParenthesis); + } + else + { + // TODO: We can actually do this in one op using mix(). + int srcElem = 0; + for (destElem = 0; destElem < 4; ++destElem) + { + int numParenthesis = 0; + if (pDest->eSelMode == OPERAND_4_COMPONENT_MASK_MODE && pDest->ui32CompMask != 0 && !(pDest->ui32CompMask & (1 << destElem))) + continue; + + AddIndentation(psContext); + GLSLAddOpAssignToDestWithMask(psContext, pDest, eDestType, 1, "=", &numParenthesis, 1 << destElem); + bcatcstr(glsl, "("); + TranslateOperandWithMask(psContext, src0, TO_AUTO_BITCAST_TO_INT, 1 << srcElem); + if (psContext->psShader->ui32MajorVersion < 4) + { + // cmp opcode uses >= 0 + bcatcstr(glsl, " >= 0) ? "); + } + else + { + bcatcstr(glsl, " != 0) ? "); + } + + TranslateOperandWithMask(psContext, src1, SVTTypeToFlag(eDestType), 1 << srcElem); + bcatcstr(glsl, " : "); + TranslateOperandWithMask(psContext, src2, SVTTypeToFlag(eDestType), 1 << srcElem); + + GLSLAddAssignPrologue(psContext, numParenthesis); + + srcElem++; + } + } +} + +// Returns nonzero if operands are identical, only cares about temp registers currently. +static int GLSLAreTempOperandsIdentical(const Operand* psA, const Operand* psB) +{ + if (!psA || !psB) + return 0; + + if (psA->eType != OPERAND_TYPE_TEMP || psB->eType != OPERAND_TYPE_TEMP) + return 0; + + if (psA->eModifier != psB->eModifier) + return 0; + + if (psA->iNumComponents != psB->iNumComponents) + return 0; + + if (psA->ui32RegisterNumber != psB->ui32RegisterNumber) + return 0; + + if (psA->eSelMode != psB->eSelMode) + return 0; + + if (psA->eSelMode == OPERAND_4_COMPONENT_MASK_MODE && psA->ui32CompMask != psB->ui32CompMask) + return 0; + + if (psA->eSelMode != OPERAND_4_COMPONENT_MASK_MODE && psA->ui32Swizzle != psB->ui32Swizzle) + return 0; + + return 1; +} + +// Returns nonzero if the operation is commutative +static int GLSLIsOperationCommutative(OPCODE_TYPE eOpCode) +{ + switch (eOpCode) + { + case OPCODE_DADD: + case OPCODE_IADD: + case OPCODE_ADD: + case OPCODE_MUL: + case OPCODE_IMUL: + case OPCODE_OR: + case OPCODE_AND: + return 1; + default: + return 0; + }; +} + +static void +GLSLCallBinaryOp(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, int dest, int src0, int src1, SHADER_VARIABLE_TYPE eDataType) +{ + bstring glsl = *psContext->currentShaderString; + uint32_t src1SwizCount = GetNumSwizzleElements(&psInst->asOperands[src1]); + uint32_t src0SwizCount = GetNumSwizzleElements(&psInst->asOperands[src0]); + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + uint32_t destMask = GetOperandWriteMask(&psInst->asOperands[dest]); + int needsParenthesis = 0; + + AddIndentation(psContext); + + if (src1SwizCount == src0SwizCount == dstSwizCount) + { + // Optimization for readability (and to make for loops in WebGL happy): detect cases where either src == dest and emit +=, -= etc. instead. + if (GLSLAreTempOperandsIdentical(&psInst->asOperands[dest], &psInst->asOperands[src0]) != 0) + { + GLSLAddOpAssignToDestWithMask(psContext, &psInst->asOperands[dest], eDataType, dstSwizCount, name, &needsParenthesis, OPERAND_4_COMPONENT_MASK_ALL); + TranslateOperand(psContext, &psInst->asOperands[src1], SVTTypeToFlag(eDataType)); + GLSLAddAssignPrologue(psContext, needsParenthesis); + return; + } + else if (GLSLAreTempOperandsIdentical(&psInst->asOperands[dest], &psInst->asOperands[src1]) != 0 && (GLSLIsOperationCommutative(psInst->eOpcode) != 0)) + { + GLSLAddOpAssignToDestWithMask(psContext, &psInst->asOperands[dest], eDataType, dstSwizCount, name, &needsParenthesis, OPERAND_4_COMPONENT_MASK_ALL); + TranslateOperand(psContext, &psInst->asOperands[src0], SVTTypeToFlag(eDataType)); + GLSLAddAssignPrologue(psContext, needsParenthesis); + return; + } + } + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], eDataType, dstSwizCount, &needsParenthesis); + + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], SVTTypeToFlag(eDataType), destMask); + bformata(glsl, " %s ", name); + TranslateOperandWithMask(psContext, &psInst->asOperands[src1], SVTTypeToFlag(eDataType), destMask); + GLSLAddAssignPrologue(psContext, needsParenthesis); +} + +static void GLSLCallTernaryOp(HLSLCrossCompilerContext* psContext, + const char* op1, + const char* op2, + Instruction* psInst, + int dest, + int src0, + int src1, + int src2, + uint32_t dataType) +{ + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + uint32_t destMask = GetOperandWriteMask(&psInst->asOperands[dest]); + + uint32_t ui32Flags = dataType; + int numParenthesis = 0; + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], TypeFlagsToSVTType(dataType), dstSwizCount, &numParenthesis); + + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bformata(glsl, " %s ", op1); + TranslateOperandWithMask(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + bformata(glsl, " %s ", op2); + TranslateOperandWithMask(psContext, &psInst->asOperands[src2], ui32Flags, destMask); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void GLSLCallHelper3(HLSLCrossCompilerContext* psContext, + const char* name, + Instruction* psInst, + int dest, + int src0, + int src1, + int src2, + int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_FLOAT; + bstring glsl = *psContext->currentShaderString; + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + int numParenthesis = 0; + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_FLOAT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[src2], ui32Flags, destMask); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void +GLSLCallHelper2(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, int dest, int src0, int src1, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_FLOAT; + bstring glsl = *psContext->currentShaderString; + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + + int isDotProduct = (strncmp(name, "dot", 3) == 0) ? 1 : 0; + int numParenthesis = 0; + + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_FLOAT, isDotProduct ? 1 : dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void +GLSLCallHelper2Int(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, int dest, int src0, int src1, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_INT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_INT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void +GLSLCallHelper2UInt(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, int dest, int src0, int src1, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_UINT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_UINT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void GLSLCallHelper1(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, int dest, int src0, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_FLOAT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_FLOAT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +// Result is an int. +static void GLSLCallHelper1Int(HLSLCrossCompilerContext* psContext, + const char* name, + Instruction* psInst, + const int dest, + const int src0, + int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_INT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_INT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void GLSLTranslateTexelFetch(HLSLCrossCompilerContext* psContext, Instruction* psInst, ResourceBinding* psBinding, bstring glsl) +{ + int numParenthesis = 0; + uint32_t destCount = GetNumSwizzleElements(&psInst->asOperands[0]); + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], TypeFlagsToSVTType(GLSLResourceReturnTypeToFlag(psBinding->ui32ReturnType)), 4, + &numParenthesis); + bcatcstr(glsl, "texelFetch("); + + switch (psBinding->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + case REFLECT_RESOURCE_DIMENSION_BUFFER: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + if (psBinding->eDimension != REFLECT_RESOURCE_DIMENSION_BUFFER) + bcatcstr(glsl, ", 0"); // Buffers don't have LOD + bcatcstr(glsl, ")"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC3, 7 /* .xyz */); + bcatcstr(glsl, ", 0)"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC2, 3 /* .xy */); + bcatcstr(glsl, ", 0)"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: // TODO does this make any sense at all? + { + ASSERT(psInst->eOpcode == OPCODE_LD_MS); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC2, 3 /* .xy */); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[3], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(glsl, ")"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + ASSERT(psInst->eOpcode == OPCODE_LD_MS); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC3, 7 /* .xyz */); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[3], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(glsl, ")"); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + case REFLECT_RESOURCE_DIMENSION_BUFFEREX: + default: + { + ASSERT(0); + break; + } + } + + AddSwizzleUsingElementCount(psContext, destCount); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static void GLSLTranslateTexelFetchOffset(HLSLCrossCompilerContext* psContext, Instruction* psInst, ResourceBinding* psBinding, bstring glsl) +{ + int numParenthesis = 0; + uint32_t destCount = GetNumSwizzleElements(&psInst->asOperands[0]); + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], TypeFlagsToSVTType(GLSLResourceReturnTypeToFlag(psBinding->ui32ReturnType)), 4, + &numParenthesis); + + bcatcstr(glsl, "texelFetchOffset("); + + switch (psBinding->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bformata(glsl, ", 0, %d)", psInst->iUAddrOffset); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC3, 7 /* .xyz */); + bformata(glsl, ", 0, ivec2(%d, %d))", psInst->iUAddrOffset, psInst->iVAddrOffset); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC3, 7 /* .xyz */); + bformata(glsl, ", 0, ivec3(%d, %d, %d))", psInst->iUAddrOffset, psInst->iVAddrOffset, psInst->iWAddrOffset); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC2, 3 /* .xy */); + bformata(glsl, ", 0, ivec2(%d, %d))", psInst->iUAddrOffset, psInst->iVAddrOffset); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC2, 3 /* .xy */); + bformata(glsl, ", 0, int(%d))", psInst->iUAddrOffset); + break; + } + case REFLECT_RESOURCE_DIMENSION_BUFFER: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + case REFLECT_RESOURCE_DIMENSION_BUFFEREX: + default: + { + ASSERT(0); + break; + } + } + + AddSwizzleUsingElementCount(psContext, destCount); + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +// Makes sure the texture coordinate swizzle is appropriate for the texture type. +// i.e. vecX for X-dimension texture. +// Currently supports floating point coord only, so not used for texelFetch. +static void GLSLTranslateTexCoord(HLSLCrossCompilerContext* psContext, const RESOURCE_DIMENSION eResDim, Operand* psTexCoordOperand) +{ + uint32_t flags = TO_AUTO_BITCAST_TO_FLOAT; + uint32_t opMask = OPERAND_4_COMPONENT_MASK_ALL; + + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + // Vec1 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X; + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + // Vec2 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y; + flags |= TO_AUTO_EXPAND_TO_VEC2; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + // Vec3 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z; + flags |= TO_AUTO_EXPAND_TO_VEC3; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + flags |= TO_AUTO_EXPAND_TO_VEC4; + break; + } + default: + { + ASSERT(0); + break; + } + } + + // FIXME detect when integer coords are needed. + TranslateOperandWithMask(psContext, psTexCoordOperand, flags, opMask); +} + +static int GLSLGetNumTextureDimensions(HLSLCrossCompilerContext* psContext, const RESOURCE_DIMENSION eResDim) +{ + (void)psContext; + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + return 1; + } + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + case RESOURCE_DIMENSION_TEXTURECUBE: + { + return 2; + } + + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + return 3; + } + default: + { + ASSERT(0); + break; + } + } + return 0; +} + +void GetResInfoData(HLSLCrossCompilerContext* psContext, Instruction* psInst, int index, int destElem) +{ + bstring glsl = *psContext->currentShaderString; + int numParenthesis = 0; + const RESINFO_RETURN_TYPE eResInfoReturnType = psInst->eResInfoReturnType; + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + + AddIndentation(psContext); + GLSLAddOpAssignToDestWithMask(psContext, &psInst->asOperands[0], eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT ? SVT_UINT : SVT_FLOAT, 1, "=", + &numParenthesis, 1 << destElem); + + //[width, height, depth or array size, total-mip-count] + if (index < 3) + { + int dim = GLSLGetNumTextureDimensions(psContext, eResDim); + bcatcstr(glsl, "("); + if (dim < (index + 1)) + { + bcatcstr(glsl, eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT ? "0u" : "0.0"); + } + else + { + if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) + { + bformata(glsl, "uvec%d(textureSize(", dim); + } + else if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_RCPFLOAT) + { + bformata(glsl, "vec%d(1.0) / vec%d(textureSize(", dim, dim); + } + else + { + bformata(glsl, "vec%d(textureSize(", dim); + } + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, "))"); + + switch (index) + { + case 0: + bcatcstr(glsl, ".x"); + break; + case 1: + bcatcstr(glsl, ".y"); + break; + case 2: + bcatcstr(glsl, ".z"); + break; + } + } + + bcatcstr(glsl, ")"); + } + else + { + if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) + bcatcstr(glsl, "uint("); + else + bcatcstr(glsl, "float("); + bcatcstr(glsl, "textureQueryLevels("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, "))"); + } + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +#define TEXSMP_FLAG_NONE 0x0 +#define TEXSMP_FLAG_LOD 0x1 // LOD comes from operand +#define TEXSMP_FLAG_DEPTHCOMPARE 0x2 +#define TEXSMP_FLAG_FIRSTLOD 0x4 // LOD is 0 +#define TEXSMP_FLAG_BIAS 0x8 +#define TEXSMP_FLAGS_GRAD 0x10 + +// TODO FIXME: non-float samplers! +static void GLSLTranslateTextureSample(HLSLCrossCompilerContext* psContext, Instruction* psInst, uint32_t ui32Flags) +{ + bstring glsl = *psContext->currentShaderString; + int numParenthesis = 0; + + const char* funcName = "texture"; + const char* offset = ""; + const char* depthCmpCoordType = ""; + const char* gradSwizzle = ""; + + uint32_t ui32NumOffsets = 0; + + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + + const int iHaveOverloadedTexFuncs = HaveOverloadedTextureFuncs(psContext->psShader->eTargetLanguage); + + const int useCombinedTextureSamplers = (psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) ? 1 : 0; + + ASSERT(psInst->asOperands[2].ui32RegisterNumber < MAX_TEXTURES); + + if (psInst->bAddressOffset) + { + offset = "Offset"; + } + + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + depthCmpCoordType = "vec2"; + gradSwizzle = ".x"; + ui32NumOffsets = 1; + if (!iHaveOverloadedTexFuncs) + { + funcName = "texture1D"; + if (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) + { + funcName = "shadow1D"; + } + } + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + depthCmpCoordType = "vec3"; + gradSwizzle = ".xy"; + ui32NumOffsets = 2; + if (!iHaveOverloadedTexFuncs) + { + funcName = "texture2D"; + if (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) + { + funcName = "shadow2D"; + } + } + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + depthCmpCoordType = "vec3"; + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + if (!iHaveOverloadedTexFuncs) + { + funcName = "textureCube"; + } + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + depthCmpCoordType = "vec4"; + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + if (!iHaveOverloadedTexFuncs) + { + funcName = "texture3D"; + } + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + depthCmpCoordType = "vec3"; + gradSwizzle = ".x"; + ui32NumOffsets = 1; + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + depthCmpCoordType = "vec4"; + gradSwizzle = ".xy"; + ui32NumOffsets = 2; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + if (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) + { + SHADER_VARIABLE_TYPE dataType = SVT_FLOAT; // TODO!! + // Special. Reference is a separate argument. + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], dataType, 1, &numParenthesis); + if (ui32Flags & (TEXSMP_FLAG_LOD | TEXSMP_FLAG_FIRSTLOD)) + { + bcatcstr(glsl, "textureLod("); + } + else + { + bcatcstr(glsl, "texture("); + } + if (!useCombinedTextureSamplers) + ResourceName(glsl, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) ? 1 : 0); + else + bconcat(glsl, TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, + psInst->asOperands[3].ui32RegisterNumber, (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) ? 1 : 0)); + bcatcstr(glsl, ","); + GLSLTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(glsl, ","); + //.z = reference. + TranslateOperand(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(glsl, ", 0.0"); + } + + bcatcstr(glsl, ")"); + // Doesn't make any sense to do swizzles here, depth comparison returns a scalar. + GLSLAddAssignPrologue(psContext, numParenthesis); + return; + } + + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) + { + // For non-cubeMap Arrays the reference value comes from the + // texture coord vector in GLSL. For cubmap arrays there is a + // separate parameter. + // It is always separate paramter in HLSL. + SHADER_VARIABLE_TYPE dataType = SVT_FLOAT; // TODO!! + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], dataType, GetNumSwizzleElements(&psInst->asOperands[2]), &numParenthesis); + if (ui32Flags & (TEXSMP_FLAG_LOD | TEXSMP_FLAG_FIRSTLOD)) + { + bformata(glsl, "%sLod%s(", funcName, offset); + } + else + { + bformata(glsl, "%s%s(", funcName, offset); + } + if (!useCombinedTextureSamplers) + ResourceName(glsl, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, 1); + else + bconcat(glsl, + TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1)); + bformata(glsl, ", %s(", depthCmpCoordType); + GLSLTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(glsl, ","); + //.z = reference. + TranslateOperand(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(glsl, ")"); + + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(glsl, ", 0.0"); + } + + bcatcstr(glsl, ")"); + } + else + { + SHADER_VARIABLE_TYPE dataType = SVT_FLOAT; // TODO!! + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], dataType, GetNumSwizzleElements(&psInst->asOperands[2]), &numParenthesis); + + if (ui32Flags & (TEXSMP_FLAG_LOD | TEXSMP_FLAG_FIRSTLOD)) + { + bformata(glsl, "%sLod%s(", funcName, offset); + } + else if (ui32Flags & TEXSMP_FLAGS_GRAD) + { + bformata(glsl, "%sGrad%s(", funcName, offset); + } + else + { + bformata(glsl, "%s%s(", funcName, offset); + } + if (!useCombinedTextureSamplers) + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); // resource + else + bconcat(glsl, + TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 0)); + bcatcstr(glsl, ", "); + GLSLTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + + if (ui32Flags & (TEXSMP_FLAG_LOD)) + { + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + if (psContext->psShader->ui32MajorVersion < 4) + { + bcatcstr(glsl, ".w"); + } + } + else if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(glsl, ", 0.0"); + } + else if (ui32Flags & TEXSMP_FLAGS_GRAD) + { + bcatcstr(glsl, ", vec4("); + TranslateOperand(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); // dx + bcatcstr(glsl, ")"); + bcatcstr(glsl, gradSwizzle); + bcatcstr(glsl, ", vec4("); + TranslateOperand(psContext, &psInst->asOperands[5], TO_AUTO_BITCAST_TO_FLOAT); // dy + bcatcstr(glsl, ")"); + bcatcstr(glsl, gradSwizzle); + } + + if (psInst->bAddressOffset) + { + if (ui32NumOffsets == 1) + { + bformata(glsl, ", %d", psInst->iUAddrOffset); + } + else if (ui32NumOffsets == 2) + { + bformata(glsl, ", ivec2(%d, %d)", psInst->iUAddrOffset, psInst->iVAddrOffset); + } + else if (ui32NumOffsets == 3) + { + bformata(glsl, ", ivec3(%d, %d, %d)", psInst->iUAddrOffset, psInst->iVAddrOffset, psInst->iWAddrOffset); + } + } + + if (ui32Flags & (TEXSMP_FLAG_BIAS)) + { + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + } + + bcatcstr(glsl, ")"); + } + + if (!(ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE)) + { + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleWithMask(psContext, &psInst->asOperands[2], GetOperandWriteMask(&psInst->asOperands[0])); + } + GLSLAddAssignPrologue(psContext, numParenthesis); +} + +static ShaderVarType* GLSLLookupStructuredVar(HLSLCrossCompilerContext* psContext, Operand* psResource, Operand* psByteOffset, uint32_t ui32Component) +{ + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + uint32_t aui32Swizzle[4] = {OPERAND_4_COMPONENT_X}; + int byteOffset = ((int*)psByteOffset->afImmediates)[0] + 4 * ui32Component; + int vec4Offset = 0; + int32_t index = -1; + int32_t rebase = -1; + int found; + + ASSERT(psByteOffset->eType == OPERAND_TYPE_IMMEDIATE32); + // TODO: multi-component stores and vector writes need testing. + + // aui32Swizzle[0] = psInst->asOperands[0].aui32Swizzle[component]; + + switch (byteOffset % 16) + { + case 0: + aui32Swizzle[0] = 0; + break; + case 4: + aui32Swizzle[0] = 1; + break; + case 8: + aui32Swizzle[0] = 2; + break; + case 12: + aui32Swizzle[0] = 3; + break; + } + + switch (psResource->eType) + { + case OPERAND_TYPE_RESOURCE: + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + GetConstantBufferFromBindingPoint(RGROUP_UAV, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + // dcl_tgsm_structured defines the amount of memory and a stride. + ASSERT(psResource->ui32RegisterNumber < MAX_GROUPSHARED); + return &psContext->psShader->sGroupSharedVarType[psResource->ui32RegisterNumber]; + } + default: + ASSERT(0); + break; + } + + found = GetShaderVarFromOffset(vec4Offset, aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + ASSERT(found); + + return psVarType; +} + +static void GLSLTranslateShaderStorageStore(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentShaderString; + ShaderVarType* psVarType = NULL; + int component; + int srcComponent = 0; + + Operand* psDest = 0; + Operand* psDestAddr = 0; + Operand* psDestByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_STORE_STRUCTURED: + psDest = &psInst->asOperands[0]; + psDestAddr = &psInst->asOperands[1]; + psDestByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + + break; + case OPCODE_STORE_RAW: + psDest = &psInst->asOperands[0]; + psDestByteOff = &psInst->asOperands[1]; + psSrc = &psInst->asOperands[2]; + break; + } + + for (component = 0; component < 4; component++) + { + ASSERT(psInst->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psInst->asOperands[0].ui32CompMask & (1 << component)) + { + + if (structured && psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + psVarType = GLSLLookupStructuredVar(psContext, psDest, psDestByteOff, component); + } + + AddIndentation(psContext); + + if (structured && psDest->eType == OPERAND_TYPE_RESOURCE) + { + bformata(glsl, "StructuredRes%d", psDest->ui32RegisterNumber); + } + else + { + TranslateOperand(psContext, psDest, TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY); + } + bformata(glsl, "["); + if (structured) // Dest address and dest byte offset + { + if (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + TranslateOperand(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, "].value["); + TranslateOperand(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, "/4u "); // bytes to floats + } + else + { + TranslateOperand(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + } + else + { + TranslateOperand(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + + // RAW: change component using index offset + if (!structured || (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY)) + { + bformata(glsl, " + %d", component); + } + + bformata(glsl, "]"); + + if (structured && psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + if (strcmp(psVarType->Name, "$Element") != 0) + { + bformata(glsl, ".%s", psVarType->Name); + } + } + + if (structured) + { + uint32_t flags = TO_FLAG_UNSIGNED_INTEGER; + if (psVarType) + { + if (psVarType->Type == SVT_INT) + { + flags = TO_FLAG_INTEGER; + } + else if (psVarType->Type == SVT_FLOAT) + { + flags = TO_FLAG_NONE; + } + } + // TGSM always uint + bformata(glsl, " = ("); + if (GetNumSwizzleElements(psSrc) > 1) + TranslateOperandWithMask(psContext, psSrc, flags, 1 << (srcComponent++)); + else + TranslateOperandWithMask(psContext, psSrc, flags, OPERAND_4_COMPONENT_MASK_X); + } + else + { + // Dest type is currently always a uint array. + bformata(glsl, " = ("); + if (GetNumSwizzleElements(psSrc) > 1) + TranslateOperandWithMask(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER, 1 << (srcComponent++)); + else + TranslateOperandWithMask(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER, OPERAND_4_COMPONENT_MASK_X); + } + + // Double takes an extra slot. + if (psVarType && psVarType->Type == SVT_DOUBLE) + { + if (structured && psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + bcatcstr(glsl, ")"); + component++; + } + + bformata(glsl, ");\n"); + } + } +} +static void GLSLTranslateShaderStorageLoad(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentShaderString; + int component; + Operand* psDest = 0; + Operand* psSrcAddr = 0; + Operand* psSrcByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_LD_STRUCTURED: + psDest = &psInst->asOperands[0]; + psSrcAddr = &psInst->asOperands[1]; + psSrcByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + break; + case OPCODE_LD_RAW: + psDest = &psInst->asOperands[0]; + psSrcByteOff = &psInst->asOperands[1]; + psSrc = &psInst->asOperands[2]; + break; + } + + if (psInst->eOpcode == OPCODE_LD_RAW) + { + int numParenthesis = 0; + int firstItemAdded = 0; + uint32_t destCount = GetNumSwizzleElements(psDest); + uint32_t destMask = GetOperandWriteMask(psDest); + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, psDest, SVT_UINT, destCount, &numParenthesis); + if (destCount > 1) + { + bformata(glsl, "%s(", GetConstructorForType(SVT_UINT, destCount)); + numParenthesis++; + } + for (component = 0; component < 4; component++) + { + if (!(destMask & (1 << component))) + continue; + + if (firstItemAdded) + bcatcstr(glsl, ", "); + else + firstItemAdded = 1; + + bformata(glsl, "RawRes%d[((", psSrc->ui32RegisterNumber); + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_INTEGER); + bcatcstr(glsl, ") >> 2)"); + if (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE && psSrc->aui32Swizzle[component] != 0) + { + bformata(glsl, " + %d", psSrc->aui32Swizzle[component]); + } + bcatcstr(glsl, "]"); + } + GLSLAddAssignPrologue(psContext, numParenthesis); + } + else + { + int numParenthesis = 0; + int firstItemAdded = 0; + uint32_t destCount = GetNumSwizzleElements(psDest); + uint32_t destMask = GetOperandWriteMask(psDest); + ASSERT(psInst->eOpcode == OPCODE_LD_STRUCTURED); + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, psDest, SVT_UINT, destCount, &numParenthesis); + if (destCount > 1) + { + bformata(glsl, "%s(", GetConstructorForType(SVT_UINT, destCount)); + numParenthesis++; + } + for (component = 0; component < 4; component++) + { + ShaderVarType* psVar = NULL; + int addedBitcast = 0; + if (!(destMask & (1 << component))) + continue; + + if (firstItemAdded) + bcatcstr(glsl, ", "); + else + firstItemAdded = 1; + + if (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + // input already in uints + TranslateOperand(psContext, psSrc, TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "["); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "].value[("); + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, " >> 2u) + %d]", psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + } + else + { + ConstantBuffer* psCBuf = NULL; + psVar = GLSLLookupStructuredVar(psContext, psSrc, psSrcByteOff, + psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + GetConstantBufferFromBindingPoint(RGROUP_UAV, psSrc->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + if (psVar->Type == SVT_FLOAT) + { + bcatcstr(glsl, "floatBitsToUint("); + addedBitcast = 1; + } + else if (psVar->Type == SVT_DOUBLE) + { + bcatcstr(glsl, "unpackDouble2x32("); + addedBitcast = 1; + } + if (psSrc->eType == OPERAND_TYPE_UNORDERED_ACCESS_VIEW) + { + bformata(glsl, "%s[", psCBuf->Name); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + if (strcmp(psVar->Name, "$Element") != 0) + { + bcatcstr(glsl, "."); + bcatcstr(glsl, psVar->Name); + } + } + else + { + bformata(glsl, "StructuredRes%d[", psSrc->ui32RegisterNumber); + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(glsl, "]."); + + bcatcstr(glsl, psVar->Name); + } + + if (addedBitcast) + bcatcstr(glsl, ")"); + if (psVar->Type == SVT_DOUBLE) + component++; // doubles take up 2 slots + } + } + GLSLAddAssignPrologue(psContext, numParenthesis); + + return; + } + +#if 0 + + //(int)GetNumSwizzleElements(&psInst->asOperands[0]) + for (component = 0; component < 4; component++) + { + const char* swizzleString[] = { ".x", ".y", ".z", ".w" }; + ASSERT(psDest->eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psDest->ui32CompMask & (1 << component)) + { + if (structured && psSrc->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + psVarType = GLSLLookupStructuredVar(psContext, psSrc, psSrcByteOff, psSrc->aui32Swizzle[component]); + } + + AddIndentation(psContext); + + aui32Swizzle[0] = psSrc->aui32Swizzle[component]; + + TranslateOperand(psContext, psDest, TO_FLAG_DESTINATION); + if (GetNumSwizzleElements(psDest) > 1) + bformata(glsl, swizzleString[destComponent++]); + + if (psVarType) + { + // TODO completely broken now after GLSLMETALAddAssignToDest refactorings. + GLSLMETALAddAssignToDest(psContext, psDest, SVTTypeToFlag(psVarType->Type), GetNumSwizzleElements(psDest), &numParenthesis); + } + else + { + GLSLMETALAddAssignToDest(psContext, psDest, TO_FLAG_NONE, GetNumSwizzleElements(psDest), &numParenthesis); + } + + if (psSrc->eType == OPERAND_TYPE_RESOURCE) + { + if (structured) + bformata(glsl, "(StructuredRes%d[", psSrc->ui32RegisterNumber); + else + bformata(glsl, "(RawRes%d[", psSrc->ui32RegisterNumber); + } + else + { + bformata(glsl, "("); + TranslateOperand(psContext, psSrc, TO_FLAG_NAME_ONLY); + bformata(glsl, "["); + Translate + } + + if (structured) //src address and src byte offset + { + if (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, "].value["); + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(glsl, "/4u ");//bytes to floats + } + else + { + TranslateOperand(psContext, psSrcAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + } + else + { + TranslateOperand(psContext, psSrcByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + + //RAW: change component using index offset + if (!structured || (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY)) + { + bformata(glsl, " + %d", psSrc->aui32Swizzle[component]); + } + + bformata(glsl, "]"); + if (structured && psSrc->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + if (strcmp(psVarType->Name, "$Element") != 0) + { + bformata(glsl, ".%s", psVarType->Name); + } + + if (psVarType->Type == SVT_DOUBLE) + { + //Double takes an extra slot. + component++; + } + } + + bformata(glsl, ");\n"); + } + } +#endif +} + +void TranslateAtomicMemOp(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring glsl = *psContext->currentShaderString; + int numParenthesis = 0; + ShaderVarType* psVarType = NULL; + uint32_t ui32DataTypeFlag = TO_FLAG_INTEGER; + const char* func = ""; + Operand* dest = 0; + Operand* previousValue = 0; + Operand* destAddr = 0; + Operand* src = 0; + Operand* compare = 0; + + switch (psInst->eOpcode) + { + case OPCODE_IMM_ATOMIC_IADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_IADD\n"); +#endif + func = "atomicAdd"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_IADD\n"); +#endif + func = "atomicAdd"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_AND\n"); +#endif + func = "atomicAnd"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_AND\n"); +#endif + func = "atomicAnd"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_OR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_OR\n"); +#endif + func = "atomicOr"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_OR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_OR\n"); +#endif + func = "atomicOr"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_XOR\n"); +#endif + func = "atomicXor"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_XOR\n"); +#endif + func = "atomicXor"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + + case OPCODE_IMM_ATOMIC_EXCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_EXCH\n"); +#endif + func = "atomicExchange"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_CMP_EXC\n"); +#endif + func = "atomicCompSwap"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + compare = &psInst->asOperands[3]; + src = &psInst->asOperands[4]; + break; + } + case OPCODE_ATOMIC_CMP_STORE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_CMP_STORE\n"); +#endif + func = "atomicCompSwap"; + previousValue = 0; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + compare = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_IMM_ATOMIC_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_UMIN\n"); +#endif + func = "atomicMin"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_UMIN\n"); +#endif + func = "atomicMin"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_IMIN\n"); +#endif + func = "atomicMin"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_IMIN\n"); +#endif + func = "atomicMin"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_UMAX\n"); +#endif + func = "atomicMax"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_UMAX\n"); +#endif + func = "atomicMax"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_IMAX\n"); +#endif + func = "atomicMax"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ATOMIC_IMAX\n"); +#endif + func = "atomicMax"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + } + + AddIndentation(psContext); + + psVarType = GLSLLookupStructuredVar(psContext, dest, destAddr, 0); + if (psVarType->Type == SVT_UINT) + { + ui32DataTypeFlag = TO_FLAG_UNSIGNED_INTEGER | TO_AUTO_BITCAST_TO_UINT; + } + else + { + ui32DataTypeFlag = TO_FLAG_INTEGER | TO_AUTO_BITCAST_TO_INT; + } + + if (previousValue) + { + GLSLMETALAddAssignToDest(psContext, previousValue, psVarType->Type, 1, &numParenthesis); + } + bcatcstr(glsl, func); + bformata(glsl, "("); + ResourceName(glsl, psContext, RGROUP_UAV, dest->ui32RegisterNumber, 0); + bformata(glsl, "[0]"); + if (strcmp(psVarType->Name, "$Element") != 0) + { + bformata(glsl, ".%s", psVarType->Name); + } + + bcatcstr(glsl, ", "); + + if (compare) + { + TranslateOperand(psContext, compare, ui32DataTypeFlag); + bcatcstr(glsl, ", "); + } + + TranslateOperand(psContext, src, ui32DataTypeFlag); + bcatcstr(glsl, ")"); + if (previousValue) + { + GLSLAddAssignPrologue(psContext, numParenthesis); + } + else + bcatcstr(glsl, ";\n"); +} + +static void GLSLTranslateConditional(HLSLCrossCompilerContext* psContext, Instruction* psInst, bstring glsl) +{ + const char* statement = ""; + if (psInst->eOpcode == OPCODE_BREAKC) + { + statement = "break"; + } + else if (psInst->eOpcode == OPCODE_CONTINUEC) + { + statement = "continue"; + } + else if (psInst->eOpcode == OPCODE_RETC) + { + statement = "return"; + } + + if (psContext->psShader->ui32MajorVersion < 4) + { + bcatcstr(glsl, "if("); + + TranslateOperand(psContext, &psInst->asOperands[0], SVTTypeToFlag(GetOperandDataType(psContext, &psInst->asOperands[0]))); + switch (psInst->eDX9TestType) + { + case D3DSPC_GT: + { + bcatcstr(glsl, " > "); + break; + } + case D3DSPC_EQ: + { + bcatcstr(glsl, " == "); + break; + } + case D3DSPC_GE: + { + bcatcstr(glsl, " >= "); + break; + } + case D3DSPC_LT: + { + bcatcstr(glsl, " < "); + break; + } + case D3DSPC_NE: + { + bcatcstr(glsl, " != "); + break; + } + case D3DSPC_LE: + { + bcatcstr(glsl, " <= "); + break; + } + case D3DSPC_BOOLEAN: + { + bcatcstr(glsl, " != 0"); + break; + } + default: + { + break; + } + } + + if (psInst->eDX9TestType != D3DSPC_BOOLEAN) + { + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + } + + if (psInst->eOpcode != OPCODE_IF) + { + bformata(glsl, "){ %s; }\n", statement); + } + else + { + bcatcstr(glsl, "){\n"); + } + } + else + { + if (psInst->eBooleanTestType == INSTRUCTION_TEST_ZERO) + { + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER); + + if (psInst->eOpcode != OPCODE_IF) + { + bformata(glsl, ")==0u){%s;}\n", statement); + } + else + { + bcatcstr(glsl, ")==0u){\n"); + } + } + else + { + ASSERT(psInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO); + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER); + + if (psInst->eOpcode != OPCODE_IF) + { + bformata(glsl, ")!=0u){%s;}\n", statement); + } + else + { + bcatcstr(glsl, ")!=0u){\n"); + } + } + } +} + +// Returns the "more important" type of a and b, currently int < uint < float +static SHADER_VARIABLE_TYPE GLSLSelectHigherType(SHADER_VARIABLE_TYPE a, SHADER_VARIABLE_TYPE b) +{ + if (a == SVT_FLOAT || b == SVT_FLOAT) + return SVT_FLOAT; + // Apart from floats, the enum values are fairly well-ordered, use that directly. + return a > b ? a : b; +} + +// Helper function to set the vector type of 1 or more components in a vector +// If the existing values (that we're writing to) are all SVT_VOID, just upgrade the value and we're done +// Otherwise, set all the components in the vector that currently are set to that same value OR are now being written to +// to the "highest" type value (ordering int->uint->float) +static void GLSLSetVectorType(SHADER_VARIABLE_TYPE* aeTempVecType, uint32_t regBaseIndex, uint32_t componentMask, SHADER_VARIABLE_TYPE eType) +{ + int existingTypesFound = 0; + int i = 0; + for (i = 0; i < 4; i++) + { + if (componentMask & (1 << i)) + { + if (aeTempVecType[regBaseIndex + i] != SVT_VOID) + { + existingTypesFound = 1; + break; + } + } + } + + if (existingTypesFound != 0) + { + // Expand the mask to include all components that are used, also upgrade type + for (i = 0; i < 4; i++) + { + if (aeTempVecType[regBaseIndex + i] != SVT_VOID) + { + componentMask |= (1 << i); + eType = GLSLSelectHigherType(eType, aeTempVecType[regBaseIndex + i]); + } + } + } + + // Now componentMask contains the components we actually need to update and eType may have been changed to something else. + // Write the results + for (i = 0; i < 4; i++) + { + if (componentMask & (1 << i)) + { + aeTempVecType[regBaseIndex + i] = eType; + } + } +} + +static void GLSLMarkOperandAs(Operand* psOperand, SHADER_VARIABLE_TYPE eType, SHADER_VARIABLE_TYPE* aeTempVecType) +{ + if (psOperand->eType == OPERAND_TYPE_INDEXABLE_TEMP || psOperand->eType == OPERAND_TYPE_TEMP) + { + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + GLSLSetVectorType(aeTempVecType, ui32RegIndex, 1 << psOperand->aui32Swizzle[0], eType); + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + // 0xf == all components, swizzle order doesn't matter. + GLSLSetVectorType(aeTempVecType, ui32RegIndex, 0xf, eType); + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + GLSLSetVectorType(aeTempVecType, ui32RegIndex, ui32CompMask, eType); + } + } +} + +static void GLSLMarkAllOperandsAs(Instruction* psInst, SHADER_VARIABLE_TYPE eType, SHADER_VARIABLE_TYPE* aeTempVecType) +{ + uint32_t i = 0; + for (i = 0; i < psInst->ui32NumOperands; i++) + { + GLSLMarkOperandAs(&psInst->asOperands[i], eType, aeTempVecType); + } +} + +static void GLSLWriteOperandTypes(Operand* psOperand, const SHADER_VARIABLE_TYPE* aeTempVecType) +{ + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + + if (psOperand->eType != OPERAND_TYPE_TEMP) + return; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + psOperand->aeDataType[0] = aeTempVecType[ui32RegIndex]; + psOperand->aeDataType[1] = aeTempVecType[ui32RegIndex + 1]; + psOperand->aeDataType[2] = aeTempVecType[ui32RegIndex + 2]; + psOperand->aeDataType[3] = aeTempVecType[ui32RegIndex + 3]; + } + else + { + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + psOperand->aeDataType[psOperand->aui32Swizzle[1]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[1]]; + psOperand->aeDataType[psOperand->aui32Swizzle[2]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[2]]; + psOperand->aeDataType[psOperand->aui32Swizzle[3]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[3]]; + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + int c = 0; + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + for (; c < 4; ++c) + { + if (ui32CompMask & (1 << c)) + { + psOperand->aeDataType[c] = aeTempVecType[ui32RegIndex + c]; + } + } + } +} + +// Mark scalars from CBs. TODO: Do we need to do the same for vec2/3's as well? There may be swizzles involved which make it vec4 or something else again. +static void GLSLSetCBOperandComponents(HLSLCrossCompilerContext* psContext, Operand* psOperand) +{ + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + int rebase = 0; + + if (psOperand->eType != OPERAND_TYPE_CONSTANT_BUFFER) + return; + + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + + if (psVarType->Class == SVC_SCALAR) + psOperand->iNumComponents = 1; +} + +void SetDataTypes(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount) +{ + int32_t i; + Instruction* psFirstInst = psInst; + + SHADER_VARIABLE_TYPE aeTempVecType[MAX_TEMP_VEC4 * 4]; + + if (psContext->psShader->ui32MajorVersion <= 3) + { + for (i = 0; i < MAX_TEMP_VEC4 * 4; ++i) + { + aeTempVecType[i] = SVT_FLOAT; + } + } + else + { + // Start with void, then move up the chain void->int->uint->float + for (i = 0; i < MAX_TEMP_VEC4 * 4; ++i) + { + aeTempVecType[i] = SVT_VOID; + } + } + + // if (psContext->psShader->ui32MajorVersion <= 3) + { + // First pass, do analysis: deduce the data type based on opcodes, fill out aeTempVecType table + // Only ever to int->float promotion (or int->uint), never the other way around + for (i = 0; i < i32InstCount; ++i, psInst++) + { + if (psInst->ui32NumOperands == 0) + continue; + + switch (psInst->eOpcode) + { + // All float-only ops + case OPCODE_ADD: + case OPCODE_DERIV_RTX: + case OPCODE_DERIV_RTY: + case OPCODE_DIV: + case OPCODE_DP2: + case OPCODE_DP3: + case OPCODE_DP4: + case OPCODE_EQ: + case OPCODE_EXP: + case OPCODE_FRC: + case OPCODE_LOG: + case OPCODE_MAD: + case OPCODE_MIN: + case OPCODE_MAX: + case OPCODE_MUL: + case OPCODE_NE: + case OPCODE_ROUND_NE: + case OPCODE_ROUND_NI: + case OPCODE_ROUND_PI: + case OPCODE_ROUND_Z: + case OPCODE_RSQ: + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_D: + case OPCODE_SAMPLE_B: + case OPCODE_SQRT: + case OPCODE_SINCOS: + case OPCODE_LOD: + case OPCODE_GATHER4: + + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_GATHER4_C: + case OPCODE_GATHER4_PO: + case OPCODE_GATHER4_PO_C: + case OPCODE_RCP: + + GLSLMarkAllOperandsAs(psInst, SVT_FLOAT, aeTempVecType); + break; + + // Int-only ops, no need to do anything + case OPCODE_AND: + case OPCODE_BREAKC: + case OPCODE_CALLC: + case OPCODE_CONTINUEC: + case OPCODE_IADD: + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INE: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_IF: + case OPCODE_NOT: + case OPCODE_OR: + case OPCODE_RETC: + case OPCODE_XOR: + case OPCODE_BUFINFO: + case OPCODE_COUNTBITS: + case OPCODE_FIRSTBIT_HI: + case OPCODE_FIRSTBIT_LO: + case OPCODE_FIRSTBIT_SHI: + case OPCODE_UBFE: + case OPCODE_IBFE: + case OPCODE_BFI: + case OPCODE_BFREV: + case OPCODE_ATOMIC_AND: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_IMAX: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_MOV: + case OPCODE_MOVC: + case OPCODE_SWAPC: + GLSLMarkAllOperandsAs(psInst, SVT_INT, aeTempVecType); + break; + // uint ops + case OPCODE_UDIV: + case OPCODE_ULT: + case OPCODE_UGE: + case OPCODE_UMUL: + case OPCODE_UMAD: + case OPCODE_UMAX: + case OPCODE_UMIN: + case OPCODE_USHR: + case OPCODE_UADDC: + case OPCODE_USUBB: + GLSLMarkAllOperandsAs(psInst, SVT_UINT, aeTempVecType); + break; + + // Need special handling + case OPCODE_FTOI: + case OPCODE_FTOU: + GLSLMarkOperandAs(&psInst->asOperands[0], psInst->eOpcode == OPCODE_FTOI ? SVT_INT : SVT_UINT, aeTempVecType); + GLSLMarkOperandAs(&psInst->asOperands[1], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_GE: + case OPCODE_LT: + GLSLMarkOperandAs(&psInst->asOperands[0], SVT_UINT, aeTempVecType); + GLSLMarkOperandAs(&psInst->asOperands[1], SVT_FLOAT, aeTempVecType); + GLSLMarkOperandAs(&psInst->asOperands[2], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_ITOF: + case OPCODE_UTOF: + GLSLMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + GLSLMarkOperandAs(&psInst->asOperands[1], psInst->eOpcode == OPCODE_ITOF ? SVT_INT : SVT_UINT, aeTempVecType); + break; + + case OPCODE_LD: + case OPCODE_LD_MS: + // TODO: Would need to know the sampler return type + GLSLMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_RESINFO: + { + if (psInst->eResInfoReturnType != RESINFO_INSTRUCTION_RETURN_UINT) + GLSLMarkAllOperandsAs(psInst, SVT_FLOAT, aeTempVecType); + break; + } + + case OPCODE_SAMPLE_INFO: + // TODO decode the _uint flag + GLSLMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_SAMPLE_POS: + GLSLMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_LD_UAV_TYPED: + case OPCODE_STORE_UAV_TYPED: + case OPCODE_LD_RAW: + case OPCODE_STORE_RAW: + case OPCODE_LD_STRUCTURED: + case OPCODE_STORE_STRUCTURED: + GLSLMarkOperandAs(&psInst->asOperands[0], SVT_INT, aeTempVecType); + break; + + case OPCODE_F32TOF16: + case OPCODE_F16TOF32: + // TODO + break; + + // No-operands, should never get here anyway + /* case OPCODE_BREAK: + case OPCODE_CALL: + case OPCODE_CASE: + case OPCODE_CONTINUE: + case OPCODE_CUT: + case OPCODE_DEFAULT: + case OPCODE_DISCARD: + case OPCODE_ELSE: + case OPCODE_EMIT: + case OPCODE_EMITTHENCUT: + case OPCODE_ENDIF: + case OPCODE_ENDLOOP: + case OPCODE_ENDSWITCH: + + case OPCODE_LABEL: + case OPCODE_LOOP: + case OPCODE_CUSTOMDATA: + case OPCODE_NOP: + case OPCODE_RET: + case OPCODE_SWITCH: + case OPCODE_DCL_RESOURCE: // DCL* opcodes have + case OPCODE_DCL_CONSTANT_BUFFER: // custom operand formats. + case OPCODE_DCL_SAMPLER: + case OPCODE_DCL_INDEX_RANGE: + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + case OPCODE_DCL_INPUT: + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_SIV: + case OPCODE_DCL_INPUT_PS: + case OPCODE_DCL_INPUT_PS_SGV: + case OPCODE_DCL_INPUT_PS_SIV: + case OPCODE_DCL_OUTPUT: + case OPCODE_DCL_OUTPUT_SGV: + case OPCODE_DCL_OUTPUT_SIV: + case OPCODE_DCL_TEMPS: + case OPCODE_DCL_INDEXABLE_TEMP: + case OPCODE_DCL_GLOBAL_FLAGS: + + + case OPCODE_HS_DECLS: // token marks beginning of HS sub-shader + case OPCODE_HS_CONTROL_POINT_PHASE: // token marks beginning of HS sub-shader + case OPCODE_HS_FORK_PHASE: // token marks beginning of HS sub-shader + case OPCODE_HS_JOIN_PHASE: // token marks beginning of HS sub-shader + + case OPCODE_EMIT_STREAM: + case OPCODE_CUT_STREAM: + case OPCODE_EMITTHENCUT_STREAM: + case OPCODE_INTERFACE_CALL: + + + case OPCODE_DCL_STREAM: + case OPCODE_DCL_FUNCTION_BODY: + case OPCODE_DCL_FUNCTION_TABLE: + case OPCODE_DCL_INTERFACE: + + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + case OPCODE_DCL_TESS_DOMAIN: + case OPCODE_DCL_TESS_PARTITIONING: + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + case OPCODE_DCL_HS_MAX_TESSFACTOR: + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + case OPCODE_DCL_HS_JOIN_PHASE_INSTANCE_COUNT: + + case OPCODE_DCL_THREAD_GROUP: + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW: + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + case OPCODE_DCL_RESOURCE_RAW: + case OPCODE_DCL_RESOURCE_STRUCTURED: + case OPCODE_SYNC: + + // TODO + case OPCODE_DADD: + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DMOV: + case OPCODE_DMOVC: + case OPCODE_DTOF: + case OPCODE_FTOD: + + case OPCODE_EVAL_SNAPPED: + case OPCODE_EVAL_SAMPLE_INDEX: + case OPCODE_EVAL_CENTROID: + + case OPCODE_DCL_GS_INSTANCE_COUNT: + + case OPCODE_ABORT: + case OPCODE_DEBUG_BREAK:*/ + + default: + break; + } + } + } + + // Fill the rest of aeTempVecType, just in case. + for (i = 0; i < MAX_TEMP_VEC4 * 4; i++) + { + if (aeTempVecType[i] == SVT_VOID) + aeTempVecType[i] = SVT_INT; + } + + // Now the aeTempVecType table has been filled with (mostly) valid data, write it back to all operands + psInst = psFirstInst; + for (i = 0; i < i32InstCount; ++i, psInst++) + { + int k = 0; + + if (psInst->ui32NumOperands == 0) + continue; + + // Preserve the current type on dest array index + if (psInst->asOperands[0].eType == OPERAND_TYPE_INDEXABLE_TEMP) + { + Operand* psSubOperand = psInst->asOperands[0].psSubOperand[1]; + if (psSubOperand != 0) + { + GLSLWriteOperandTypes(psSubOperand, aeTempVecType); + } + } + if (psInst->asOperands[0].eType == OPERAND_TYPE_CONSTANT_BUFFER) + GLSLSetCBOperandComponents(psContext, &psInst->asOperands[0]); + + // Preserve the current type on sources. + for (k = psInst->ui32NumOperands - 1; k >= (int)psInst->ui32FirstSrc; --k) + { + int32_t subOperand; + Operand* psOperand = &psInst->asOperands[k]; + + GLSLWriteOperandTypes(psOperand, aeTempVecType); + if (psOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + GLSLSetCBOperandComponents(psContext, psOperand); + + for (subOperand = 0; subOperand < MAX_SUB_OPERANDS; subOperand++) + { + if (psOperand->psSubOperand[subOperand] != 0) + { + Operand* psSubOperand = psOperand->psSubOperand[subOperand]; + GLSLWriteOperandTypes(psSubOperand, aeTempVecType); + if (psSubOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + GLSLSetCBOperandComponents(psContext, psSubOperand); + } + } + + // Set immediates + if (GLSLIsIntegerImmediateOpcode(psInst->eOpcode)) + { + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32) + { + psOperand->iIntegerImmediate = 1; + } + } + } + + // Process the destination last in order to handle instructions + // where the destination register is also used as a source. + for (k = 0; k < (int)psInst->ui32FirstSrc; ++k) + { + Operand* psOperand = &psInst->asOperands[k]; + GLSLWriteOperandTypes(psOperand, aeTempVecType); + } + } +} + +void TranslateInstruction(HLSLCrossCompilerContext* psContext, Instruction* psInst, Instruction* psNextInst) +{ + bstring glsl = *psContext->currentShaderString; + int numParenthesis = 0; + +#ifdef _DEBUG + AddIndentation(psContext); + bformata(glsl, "//Instruction %d\n", psInst->id); +#if 0 + if(psInst->id == 73) + { + ASSERT(1); //Set breakpoint here to debug an instruction from its ID. + } +#endif +#endif + + switch (psInst->eOpcode) + { + case OPCODE_FTOI: + case OPCODE_FTOU: + { + uint32_t dstCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t srcCount = GetNumSwizzleElements(&psInst->asOperands[1]); + +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_FTOU) + bcatcstr(glsl, "//FTOU\n"); + else + bcatcstr(glsl, "//FTOI\n"); +#endif + + AddIndentation(psContext); + + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], psInst->eOpcode == OPCODE_FTOU ? SVT_UINT : SVT_INT, srcCount, &numParenthesis); + bcatcstr(glsl, GetConstructorForType(psInst->eOpcode == OPCODE_FTOU ? SVT_UINT : SVT_INT, srcCount == dstCount ? dstCount : 4)); + bcatcstr(glsl, "("); // 1 + TranslateOperand(psContext, &psInst->asOperands[1], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(glsl, ")"); // 1 + // Add destination writemask if the component counts do not match + if (srcCount != dstCount) + AddSwizzleUsingElementCount(psContext, dstCount); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + + case OPCODE_MOV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MOV\n"); +#endif + AddIndentation(psContext); + GLSLAddMOVBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[1]); + break; + } + case OPCODE_ITOF: // signed to float + case OPCODE_UTOF: // unsigned to float + { + uint32_t dstCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t srcCount = GetNumSwizzleElements(&psInst->asOperands[1]); + +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_ITOF) + { + bcatcstr(glsl, "//ITOF\n"); + } + else + { + bcatcstr(glsl, "//UTOF\n"); + } +#endif + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, srcCount, &numParenthesis); + bcatcstr(glsl, GetConstructorForType(SVT_FLOAT, srcCount == dstCount ? dstCount : 4)); + bcatcstr(glsl, "("); // 1 + TranslateOperand(psContext, &psInst->asOperands[1], psInst->eOpcode == OPCODE_UTOF ? TO_AUTO_BITCAST_TO_UINT : TO_AUTO_BITCAST_TO_INT); + bcatcstr(glsl, ")"); // 1 + // Add destination writemask if the component counts do not match + if (srcCount != dstCount) + AddSwizzleUsingElementCount(psContext, dstCount); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_MAD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MAD\n"); +#endif + GLSLCallTernaryOp(psContext, "*", "+", psInst, 0, 1, 2, 3, TO_FLAG_NONE); + break; + } + case OPCODE_IMAD: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMAD\n"); +#endif + + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + + GLSLCallTernaryOp(psContext, "*", "+", psInst, 0, 1, 2, 3, ui32Flags); + break; + } + case OPCODE_DADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DADD\n"); +#endif + GLSLCallBinaryOp(psContext, "+", psInst, 0, 1, 2, SVT_DOUBLE); + break; + } + case OPCODE_IADD: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IADD\n"); +#endif + // Is this a signed or unsigned add? + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + eType = SVT_UINT; + } + GLSLCallBinaryOp(psContext, "+", psInst, 0, 1, 2, eType); + break; + } + case OPCODE_ADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ADD\n"); +#endif + GLSLCallBinaryOp(psContext, "+", psInst, 0, 1, 2, SVT_FLOAT); + break; + } + case OPCODE_OR: + { + /*Todo: vector version */ +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//OR\n"); +#endif + GLSLCallBinaryOp(psContext, "|", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//AND\n"); +#endif + GLSLCallBinaryOp(psContext, "&", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_GE: + { + /* + dest = vec4(greaterThanEqual(vec4(srcA), vec4(srcB)); + Caveat: The result is a boolean but HLSL asm returns 0xFFFFFFFF/0x0 instead. + */ +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GE\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_GE, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_MUL: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MUL\n"); +#endif + GLSLCallBinaryOp(psContext, "*", psInst, 0, 1, 2, SVT_FLOAT); + break; + } + case OPCODE_IMUL: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMUL\n"); +#endif + if (GetOperandDataType(psContext, &psInst->asOperands[1]) == SVT_UINT) + { + eType = SVT_UINT; + } + + ASSERT(psInst->asOperands[0].eType == OPERAND_TYPE_NULL); + + GLSLCallBinaryOp(psContext, "*", psInst, 1, 2, 3, eType); + break; + } + case OPCODE_UDIV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//UDIV\n"); +#endif + // destQuotient, destRemainder, src0, src1 + GLSLCallBinaryOp(psContext, "/", psInst, 0, 2, 3, SVT_UINT); + GLSLCallBinaryOp(psContext, "%", psInst, 1, 2, 3, SVT_UINT); + break; + } + case OPCODE_DIV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DIV\n"); +#endif + GLSLCallBinaryOp(psContext, "/", psInst, 0, 1, 2, SVT_FLOAT); + break; + } + case OPCODE_SINCOS: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SINCOS\n"); +#endif + // Need careful ordering if src == dest[0], as then the cos() will be reading from wrong value + if (psInst->asOperands[0].eType == psInst->asOperands[2].eType && + psInst->asOperands[0].ui32RegisterNumber == psInst->asOperands[2].ui32RegisterNumber) + { + // sin() result overwrites source, do cos() first. + // The case where both write the src shouldn't really happen anyway. + if (psInst->asOperands[1].eType != OPERAND_TYPE_NULL) + { + GLSLCallHelper1(psContext, "cos", psInst, 1, 2, 1); + } + + if (psInst->asOperands[0].eType != OPERAND_TYPE_NULL) + { + GLSLCallHelper1(psContext, "sin", psInst, 0, 2, 1); + } + } + else + { + if (psInst->asOperands[0].eType != OPERAND_TYPE_NULL) + { + GLSLCallHelper1(psContext, "sin", psInst, 0, 2, 1); + } + + if (psInst->asOperands[1].eType != OPERAND_TYPE_NULL) + { + GLSLCallHelper1(psContext, "cos", psInst, 1, 2, 1); + } + } + break; + } + + case OPCODE_DP2: + { + int numParenthesis2 = 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP2\n"); +#endif + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, 1, &numParenthesis2); + bcatcstr(glsl, "dot("); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_AUTO_BITCAST_TO_FLOAT, 3 /* .xy */); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[2], TO_AUTO_BITCAST_TO_FLOAT, 3 /* .xy */); + bcatcstr(glsl, ")"); + GLSLAddAssignPrologue(psContext, numParenthesis2); + break; + } + case OPCODE_DP3: + { + int numParenthesis2 = 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP3\n"); +#endif + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, 1, &numParenthesis2); + bcatcstr(glsl, "dot("); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_AUTO_BITCAST_TO_FLOAT, 7 /* .xyz */); + bcatcstr(glsl, ", "); + TranslateOperandWithMask(psContext, &psInst->asOperands[2], TO_AUTO_BITCAST_TO_FLOAT, 7 /* .xyz */); + bcatcstr(glsl, ")"); + GLSLAddAssignPrologue(psContext, numParenthesis2); + break; + } + case OPCODE_DP4: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP4\n"); +#endif + GLSLCallHelper2(psContext, "dot", psInst, 0, 1, 2, 0); + break; + } + case OPCODE_INE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INE\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_NE, TO_FLAG_INTEGER, NULL); + break; + } + case OPCODE_NE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//NE\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_NE, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_IGE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IGE\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_GE, TO_FLAG_INTEGER, psNextInst); + break; + } + case OPCODE_ILT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ILT\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_LT, TO_FLAG_INTEGER, NULL); + break; + } + case OPCODE_LT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LT\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_LT, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_IEQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IEQ\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_EQ, TO_FLAG_INTEGER, NULL); + break; + } + case OPCODE_ULT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ULT\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_LT, TO_FLAG_UNSIGNED_INTEGER, NULL); + break; + } + case OPCODE_UGE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//UGE\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_GE, TO_FLAG_UNSIGNED_INTEGER, NULL); + break; + } + case OPCODE_MOVC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MOVC\n"); +#endif + GLSLAddMOVCBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[1], &psInst->asOperands[2], &psInst->asOperands[3]); + break; + } + case OPCODE_SWAPC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SWAPC\n"); +#endif + // TODO needs temps!! + GLSLAddMOVCBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[2], &psInst->asOperands[4], &psInst->asOperands[3]); + GLSLAddMOVCBinaryOp(psContext, &psInst->asOperands[1], &psInst->asOperands[2], &psInst->asOperands[3], &psInst->asOperands[4]); + break; + } + + case OPCODE_LOG: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LOG\n"); +#endif + GLSLCallHelper1(psContext, "log2", psInst, 0, 1, 1); + break; + } + case OPCODE_RSQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RSQ\n"); +#endif + GLSLCallHelper1(psContext, "inversesqrt", psInst, 0, 1, 1); + break; + } + case OPCODE_EXP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EXP\n"); +#endif + GLSLCallHelper1(psContext, "exp2", psInst, 0, 1, 1); + break; + } + case OPCODE_SQRT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SQRT\n"); +#endif + GLSLCallHelper1(psContext, "sqrt", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_PI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_PI\n"); +#endif + GLSLCallHelper1(psContext, "ceil", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_NI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_NI\n"); +#endif + GLSLCallHelper1(psContext, "floor", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_Z: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_Z\n"); +#endif + GLSLCallHelper1(psContext, "trunc", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_NE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ROUND_NE\n"); +#endif + GLSLCallHelper1(psContext, "roundEven", psInst, 0, 1, 1); + break; + } + case OPCODE_FRC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FRC\n"); +#endif + GLSLCallHelper1(psContext, "fract", psInst, 0, 1, 1); + break; + } + case OPCODE_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMAX\n"); +#endif + GLSLCallHelper2Int(psContext, "max", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_MAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MAX\n"); +#endif + GLSLCallHelper2(psContext, "max", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMIN\n"); +#endif + GLSLCallHelper2Int(psContext, "min", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_MIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//MIN\n"); +#endif + GLSLCallHelper2(psContext, "min", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_GATHER4: + { + // dest, coords, tex, sampler + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + const int useCombinedTextureSamplers = (psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) ? 1 : 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4\n"); +#endif + // gather4 r7.xyzw, r3.xyxx, t3.xyzw, s0.x + AddIndentation(psContext); // TODO FIXME integer samplers + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, GetNumSwizzleElements(&psInst->asOperands[2]), &numParenthesis); + bcatcstr(glsl, "textureGather("); + + if (!useCombinedTextureSamplers) + ResourceName(glsl, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, 0); + else + bconcat(glsl, + TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 0)); + + bcatcstr(glsl, ", "); + GLSLTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(glsl, ")"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + + AddSwizzleUsingElementCount(psContext, GetNumSwizzleElements(&psInst->asOperands[0])); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_GATHER4_PO_C: + { + // dest, coords, offset, tex, sampler, srcReferenceValue + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[3].ui32RegisterNumber]; + const int useCombinedTextureSamplers = (psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) ? 1 : 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4_PO_C\n"); +#endif + + AddIndentation(psContext); // TODO FIXME integer samplers + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, GetNumSwizzleElements(&psInst->asOperands[2]), &numParenthesis); + bcatcstr(glsl, "textureGatherOffset("); + + if (!useCombinedTextureSamplers) + ResourceName(glsl, psContext, RGROUP_TEXTURE, psInst->asOperands[3].ui32RegisterNumber, 1); + else + bconcat(glsl, + TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1)); + + bcatcstr(glsl, ", "); + + GLSLTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[5], TO_FLAG_NONE); + + bcatcstr(glsl, ", ivec2("); + // ivec2 offset + psInst->asOperands[2].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[2].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, "))"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[3]); + AddSwizzleUsingElementCount(psContext, GetNumSwizzleElements(&psInst->asOperands[0])); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_GATHER4_PO: + { + // dest, coords, offset, tex, sampler + const int useCombinedTextureSamplers = (psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) ? 1 : 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4_PO\n"); +#endif + + AddIndentation(psContext); // TODO FIXME integer samplers + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, GetNumSwizzleElements(&psInst->asOperands[2]), &numParenthesis); + bcatcstr(glsl, "textureGatherOffset("); + + if (!useCombinedTextureSamplers) + ResourceName(glsl, psContext, RGROUP_TEXTURE, psInst->asOperands[3].ui32RegisterNumber, 0); + else + bconcat(glsl, + TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber, 0)); + + bcatcstr(glsl, ", "); + // Texture coord cannot be vec4 + // Determining if it is a vec3 for vec2 yet to be done. + psInst->asOperands[1].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[1].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + + bcatcstr(glsl, ", ivec2("); + // ivec2 offset + psInst->asOperands[2].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[2].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, "))"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[3]); + AddSwizzleUsingElementCount(psContext, GetNumSwizzleElements(&psInst->asOperands[0])); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_GATHER4_C: + { + // dest, coords, tex, sampler srcReferenceValue + const int useCombinedTextureSamplers = (psContext->flags & HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS) ? 1 : 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//GATHER4_C\n"); +#endif + + AddIndentation(psContext); // TODO FIXME integer samplers + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, GetNumSwizzleElements(&psInst->asOperands[2]), &numParenthesis); + bcatcstr(glsl, "textureGather("); + + if (!useCombinedTextureSamplers) + ResourceName(glsl, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, 1); + else + bconcat(glsl, + TextureSamplerName(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1)); + + bcatcstr(glsl, ", "); + // Texture coord cannot be vec4 + // Determining if it is a vec3 for vec2 yet to be done. + psInst->asOperands[1].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[1].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[4], TO_FLAG_NONE); + bcatcstr(glsl, ")"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzle(psContext, &psInst->asOperands[2]); + AddSwizzleUsingElementCount(psContext, GetNumSwizzleElements(&psInst->asOperands[0])); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_SAMPLE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE\n"); +#endif + GLSLTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_NONE); + break; + } + case OPCODE_SAMPLE_L: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_L\n"); +#endif + GLSLTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_LOD); + break; + } + case OPCODE_SAMPLE_C: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_C\n"); +#endif + + GLSLTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_DEPTHCOMPARE); + break; + } + case OPCODE_SAMPLE_C_LZ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_C_LZ\n"); +#endif + + GLSLTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_DEPTHCOMPARE | TEXSMP_FLAG_FIRSTLOD); + break; + } + case OPCODE_SAMPLE_D: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_D\n"); +#endif + + GLSLTranslateTextureSample(psContext, psInst, TEXSMP_FLAGS_GRAD); + break; + } + case OPCODE_SAMPLE_B: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SAMPLE_B\n"); +#endif + + GLSLTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_BIAS); + break; + } + case OPCODE_RET: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RET\n"); +#endif + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); +#endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); +#endif + } + AddIndentation(psContext); + bcatcstr(glsl, "return;\n"); + break; + } + case OPCODE_INTERFACE_CALL: + { + const char* name; + ShaderVar* psVar; + uint32_t varFound; + + uint32_t funcPointer; + uint32_t funcTableIndex; + uint32_t funcTable; + uint32_t funcBodyIndex; + uint32_t funcBody; + uint32_t ui32NumBodiesPerTable; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INTERFACE_CALL\n"); +#endif + + ASSERT(psInst->asOperands[0].eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32); + + funcPointer = psInst->asOperands[0].aui32ArraySizes[0]; + funcTableIndex = psInst->asOperands[0].aui32ArraySizes[1]; + funcBodyIndex = psInst->ui32FuncIndexWithinInterface; + + ui32NumBodiesPerTable = psContext->psShader->funcPointer[funcPointer].ui32NumBodiesPerTable; + + funcTable = psContext->psShader->funcPointer[funcPointer].aui32FuncTables[funcTableIndex]; + + funcBody = psContext->psShader->funcTable[funcTable].aui32FuncBodies[funcBodyIndex]; + + varFound = GetInterfaceVarFromOffset(funcPointer, &psContext->psShader->sInfo, &psVar); + + ASSERT(varFound); + + name = &psVar->Name[0]; + + AddIndentation(psContext); + bcatcstr(glsl, name); + TranslateOperandIndexMAD(psContext, &psInst->asOperands[0], 1, ui32NumBodiesPerTable, funcBodyIndex); + // bformata(glsl, "[%d]", funcBodyIndex); + bcatcstr(glsl, "();\n"); + break; + } + case OPCODE_LABEL: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LABEL\n"); +#endif + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); // Closing brace ends the previous function. + AddIndentation(psContext); + + bcatcstr(glsl, "subroutine(SubroutineType)\n"); + bcatcstr(glsl, "void "); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, "(){\n"); + ++psContext->indent; + break; + } + case OPCODE_COUNTBITS: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//COUNTBITS\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(glsl, " = bitCount("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_FIRSTBIT_HI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FIRSTBIT_HI\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(glsl, " = findMSB("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_FIRSTBIT_LO: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FIRSTBIT_LO\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(glsl, " = findLSB("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_FIRSTBIT_SHI: // signed high + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//FIRSTBIT_SHI\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(glsl, " = findMSB("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_BFREV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BFREV\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(glsl, " = bitfieldReverse("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_BFI: + { + uint32_t numelements_width = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t numelements_offset = GetNumSwizzleElements(&psInst->asOperands[2]); + uint32_t numelements_dest = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t numoverall_elements = min(min(numelements_width, numelements_offset), numelements_dest); + uint32_t i, j; + static const char* bfi_elementidx[] = {"x", "y", "z", "w"}; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BFI\n"); +#endif + + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_DESTINATION); + bformata(glsl, " = ivec%d(", numoverall_elements); + for (i = 0; i < numoverall_elements; ++i) + { + bcatcstr(glsl, "bitfieldInsert("); + + for (j = 4; j >= 1; --j) + { + uint32_t opSwizzleCount = GetNumSwizzleElements(&psInst->asOperands[j]); + + if (opSwizzleCount != 1) + bcatcstr(glsl, " ("); + TranslateOperand(psContext, &psInst->asOperands[j], TO_FLAG_INTEGER); + if (opSwizzleCount != 1) + bformata(glsl, " ).%s", bfi_elementidx[i]); + if (j != 1) + bcatcstr(glsl, ","); + } + + bcatcstr(glsl, ") "); + if (i + 1 != numoverall_elements) + bcatcstr(glsl, ", "); + } + + bcatcstr(glsl, ")."); + for (i = 0; i < numoverall_elements; ++i) + bformata(glsl, "%s", bfi_elementidx[i]); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_CUT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//CUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EndPrimitive();\n"); + break; + } + case OPCODE_EMIT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMIT\n"); +#endif + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); +#endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); +#endif + } + + AddIndentation(psContext); + bcatcstr(glsl, "EmitVertex();\n"); + break; + } + case OPCODE_EMITTHENCUT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMITTHENCUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EmitVertex();\nEndPrimitive();\n"); + break; + } + + case OPCODE_CUT_STREAM: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//CUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EndStreamPrimitive("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + + break; + } + case OPCODE_EMIT_STREAM: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMIT_STREAM\n"); +#endif + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- Post shader code ---\n"); +#endif + bconcat(glsl, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//--- End post shader code ---\n"); +#endif + } + + AddIndentation(psContext); + bcatcstr(glsl, "EmitStreamVertex("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_EMITTHENCUT_STREAM: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EMITTHENCUT\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "EmitStreamVertex("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + bcatcstr(glsl, "EndStreamPrimitive("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_REP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//REP\n"); +#endif + // Need to handle nesting. + // Max of 4 for rep - 'Flow Control Limitations' http://msdn.microsoft.com/en-us/library/windows/desktop/bb219848(v=vs.85).aspx + + AddIndentation(psContext); + bcatcstr(glsl, "RepCounter = "); + TranslateOperandWithMask(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(glsl, ";\n"); + + AddIndentation(psContext); + bcatcstr(glsl, "while(RepCounter!=0){\n"); + ++psContext->indent; + break; + } + case OPCODE_ENDREP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ENDREP\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "RepCounter--;\n"); + + --psContext->indent; + + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + break; + } + case OPCODE_LOOP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LOOP\n"); +#endif + AddIndentation(psContext); + + if (psInst->ui32NumOperands == 2) + { + // DX9 version + ASSERT(psInst->asOperands[0].eType == OPERAND_TYPE_SPECIAL_LOOPCOUNTER); + bcatcstr(glsl, "for("); + bcatcstr(glsl, "LoopCounter = "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ".y, ZeroBasedCounter = 0;"); + bcatcstr(glsl, "ZeroBasedCounter < "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ".x;"); + + bcatcstr(glsl, "LoopCounter += "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ".z, ZeroBasedCounter++){\n"); + ++psContext->indent; + } + else + { + bcatcstr(glsl, "while(true){\n"); + ++psContext->indent; + } + break; + } + case OPCODE_ENDLOOP: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ENDLOOP\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + break; + } + case OPCODE_BREAK: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BREAK\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "break;\n"); + break; + } + case OPCODE_BREAKC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//BREAKC\n"); +#endif + AddIndentation(psContext); + + GLSLTranslateConditional(psContext, psInst, glsl); + break; + } + case OPCODE_CONTINUEC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//CONTINUEC\n"); +#endif + AddIndentation(psContext); + + GLSLTranslateConditional(psContext, psInst, glsl); + break; + } + case OPCODE_IF: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IF\n"); +#endif + AddIndentation(psContext); + + GLSLTranslateConditional(psContext, psInst, glsl); + ++psContext->indent; + break; + } + case OPCODE_RETC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RETC\n"); +#endif + AddIndentation(psContext); + + GLSLTranslateConditional(psContext, psInst, glsl); + break; + } + case OPCODE_ELSE: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ELSE\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "} else {\n"); + psContext->indent++; + break; + } + case OPCODE_ENDSWITCH: + case OPCODE_ENDIF: + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "//ENDIF\n"); + AddIndentation(psContext); + bcatcstr(glsl, "}\n"); + break; + } + case OPCODE_CONTINUE: + { + AddIndentation(psContext); + bcatcstr(glsl, "continue;\n"); + break; + } + case OPCODE_DEFAULT: + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(glsl, "default:\n"); + ++psContext->indent; + break; + } + case OPCODE_NOP: + { + break; + } + case OPCODE_SYNC: + { + const uint32_t ui32SyncFlags = psInst->ui32SyncFlags; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SYNC\n"); +#endif + + if (ui32SyncFlags & SYNC_THREADS_IN_GROUP) + { + AddIndentation(psContext); + bcatcstr(glsl, "groupMemoryBarrier();\n"); + } + if (ui32SyncFlags & SYNC_THREAD_GROUP_SHARED_MEMORY) + { + AddIndentation(psContext); + bcatcstr(glsl, "memoryBarrierShared();\n"); + } + if (ui32SyncFlags & (SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP | SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GLOBAL)) + { + AddIndentation(psContext); + bcatcstr(glsl, "memoryBarrier();\n"); + } + break; + } + case OPCODE_SWITCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//SWITCH\n"); +#endif + AddIndentation(psContext); + bcatcstr(glsl, "switch(int("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")){\n"); + + psContext->indent += 2; + break; + } + case OPCODE_CASE: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//case\n"); +#endif + AddIndentation(psContext); + + bcatcstr(glsl, "case "); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ":\n"); + + ++psContext->indent; + break; + } + case OPCODE_EQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EQ\n"); +#endif + GLSLAddComparision(psContext, psInst, GLSL_CMP_EQ, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_USHR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//USHR\n"); +#endif + GLSLCallBinaryOp(psContext, ">>", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_ISHL: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ISHL\n"); +#endif + + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + eType = SVT_UINT; + } + + GLSLCallBinaryOp(psContext, "<<", psInst, 0, 1, 2, eType); + break; + } + case OPCODE_ISHR: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//ISHR\n"); +#endif + + if (GetOperandDataType(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + eType = SVT_UINT; + } + + GLSLCallBinaryOp(psContext, ">>", psInst, 0, 1, 2, eType); + break; + } + case OPCODE_LD: + case OPCODE_LD_MS: + { + ResourceBinding* psBinding = 0; +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_LD) + bcatcstr(glsl, "//LD\n"); + else + bcatcstr(glsl, "//LD_MS\n"); +#endif + + GetResourceFromBindingPoint(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + + if (psInst->bAddressOffset) + { + GLSLTranslateTexelFetchOffset(psContext, psInst, psBinding, glsl); + } + else + { + GLSLTranslateTexelFetch(psContext, psInst, psBinding, glsl); + } + break; + } + case OPCODE_DISCARD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DISCARD\n"); +#endif + AddIndentation(psContext); + if (psContext->psShader->ui32MajorVersion <= 3) + { + bcatcstr(glsl, "if(any(lessThan(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_NONE); + + if (psContext->psShader->ui32MajorVersion == 1) + { + /* SM1.X only kills based on the rgb channels */ + bcatcstr(glsl, ").xyz, vec3(0)))){discard;}\n"); + } + else + { + bcatcstr(glsl, "), vec4(0)))){discard;}\n"); + } + } + else if (psInst->eBooleanTestType == INSTRUCTION_TEST_ZERO) + { + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")==0){discard;}\n"); + } + else + { + ASSERT(psInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO); + bcatcstr(glsl, "if(("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")!=0){discard;}\n"); + } + break; + } + case OPCODE_LOD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LOD\n"); +#endif + // LOD computes the following vector (ClampedLOD, NonClampedLOD, 0, 0) + + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, 4, &numParenthesis); + + // If the core language does not have query-lod feature, + // then the extension is used. The name of the function + // changed between extension and core. + if (HaveQueryLod(psContext->psShader->eTargetLanguage)) + { + bcatcstr(glsl, "textureQueryLod("); + } + else + { + bcatcstr(glsl, "textureQueryLOD("); + } + + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ","); + GLSLTranslateTexCoord(psContext, psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber], &psInst->asOperands[1]); + bcatcstr(glsl, ")"); + + // The swizzle on srcResource allows the returned values to be swizzled arbitrarily before they are written to the destination. + + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleWithMask(psContext, &psInst->asOperands[2], GetOperandWriteMask(&psInst->asOperands[0])); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_EVAL_CENTROID: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EVAL_CENTROID\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = interpolateAtCentroid("); + // interpolateAtCentroid accepts in-qualified variables. + // As long as bytecode only writes vX registers in declarations + // we should be able to use the declared name directly. + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_DECLARATION_NAME); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_EVAL_SAMPLE_INDEX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EVAL_SAMPLE_INDEX\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = interpolateAtSample("); + // interpolateAtSample accepts in-qualified variables. + // As long as bytecode only writes vX registers in declarations + // we should be able to use the declared name directly. + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_DECLARATION_NAME); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_EVAL_SNAPPED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//EVAL_SNAPPED\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = interpolateAtOffset("); + // interpolateAtOffset accepts in-qualified variables. + // As long as bytecode only writes vX registers in declarations + // we should be able to use the declared name directly. + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_DECLARATION_NAME); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_INTEGER); + bcatcstr(glsl, ".xy);\n"); + break; + } + case OPCODE_LD_STRUCTURED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LD_STRUCTURED\n"); +#endif + GLSLTranslateShaderStorageLoad(psContext, psInst); + break; + } + case OPCODE_LD_UAV_TYPED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LD_UAV_TYPED\n"); +#endif + switch (psInst->eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = imageLoad("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").x)"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + bcatcstr(glsl, ";\n"); + break; + case RESOURCE_DIMENSION_TEXTURECUBE: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE2DMS: + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = imageLoad("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").xy)"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + bcatcstr(glsl, ";\n"); + break; + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = imageLoad("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, ", ("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bformata(glsl, ").xyz)"); + TranslateOperandSwizzle(psContext, &psInst->asOperands[0]); + bcatcstr(glsl, ";\n"); + break; + } + break; + } + case OPCODE_STORE_RAW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//STORE_RAW\n"); +#endif + GLSLTranslateShaderStorageStore(psContext, psInst); + break; + } + case OPCODE_STORE_STRUCTURED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//STORE_STRUCTURED\n"); +#endif + GLSLTranslateShaderStorageStore(psContext, psInst); + break; + } + + case OPCODE_STORE_UAV_TYPED: + { + ResourceBinding* psRes; + int foundResource; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//STORE_UAV_TYPED\n"); +#endif + AddIndentation(psContext); + + foundResource = GetResourceFromBindingPoint(RGROUP_UAV, psInst->asOperands[0].ui32RegisterNumber, &psContext->psShader->sInfo, &psRes); + + ASSERT(foundResource); + + bcatcstr(glsl, "imageStore("); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_NAME_ONLY); + switch (psRes->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + bcatcstr(glsl, ", int("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, "), "); + break; + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: + bcatcstr(glsl, ", ivec2("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, ".xy), "); + break; + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + bcatcstr(glsl, ", ivec3("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, ".xyz), "); + break; + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + bcatcstr(glsl, ", ivec4("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(glsl, ".xyzw) "); + break; + }; + + TranslateOperand(psContext, &psInst->asOperands[2], GLSLResourceReturnTypeToFlag(psRes->ui32ReturnType)); + bformata(glsl, ");\n"); + + break; + } + case OPCODE_LD_RAW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LD_RAW\n"); +#endif + + GLSLTranslateShaderStorageLoad(psContext, psInst); + break; + } + + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { + TranslateAtomicMemOp(psContext, psInst); + break; + } + case OPCODE_UBFE: + case OPCODE_IBFE: + { +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_UBFE) + bcatcstr(glsl, "//OPCODE_UBFE\n"); + else + bcatcstr(glsl, "//OPCODE_IBFE\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = bitfieldExtract("); + TranslateOperand(psContext, &psInst->asOperands[3], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ", "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, ");\n"); + break; + } + case OPCODE_RCP: + { + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RCP\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = (vec4(1.0) / vec4("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, "))"); + AddSwizzleUsingElementCount(psContext, destElemCount); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_F32TOF16: + { + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//F32TOF16\n"); +#endif + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = {".x", ".y", ".z", ".w"}; + + // unpackHalf2x16 converts two f16s packed into uint to two f32s. + + // dest.swiz.x = unpackHalf2x16(src.swiz.x).x + // dest.swiz.y = unpackHalf2x16(src.swiz.y).x + // dest.swiz.z = unpackHalf2x16(src.swiz.z).x + // dest.swiz.w = unpackHalf2x16(src.swiz.w).x + + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + if (destElemCount > 1) + bcatcstr(glsl, swizzle[destElem]); + + bcatcstr(glsl, " = unpackHalf2x16("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + if (s0ElemCount > 1) + bcatcstr(glsl, swizzle[destElem]); + bcatcstr(glsl, ").x;\n"); + } + break; + } + case OPCODE_F16TOF32: + { + const uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElements(&psInst->asOperands[1]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//F16TOF32\n"); +#endif + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = {".x", ".y", ".z", ".w"}; + + // packHalf2x16 converts two f32s to two f16s packed into a uint. + + // dest.swiz.x = packHalf2x16(vec2(src.swiz.x)) & 0xFFFF + // dest.swiz.y = packHalf2x16(vec2(src.swiz.y)) & 0xFFFF + // dest.swiz.z = packHalf2x16(vec2(src.swiz.z)) & 0xFFFF + // dest.swiz.w = packHalf2x16(vec2(src.swiz.w)) & 0xFFFF + + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION | TO_FLAG_UNSIGNED_INTEGER); + if (destElemCount > 1) + bcatcstr(glsl, swizzle[destElem]); + + bcatcstr(glsl, " = packHalf2x16(vec2("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + if (s0ElemCount > 1) + bcatcstr(glsl, swizzle[destElem]); + bcatcstr(glsl, ")) & 0xFFFF;\n"); + } + break; + } + case OPCODE_INEG: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INEG\n"); +#endif + // dest = 0 - src0 + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION | TO_FLAG_INTEGER); + bcatcstr(glsl, " = 0 - "); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE | TO_FLAG_INTEGER); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DERIV_RTX\n"); +#endif + GLSLCallHelper1(psContext, "dFdx", psInst, 0, 1, 1); + break; + } + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_DERIV_RTY: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DERIV_RTY\n"); +#endif + GLSLCallHelper1(psContext, "dFdy", psInst, 0, 1, 1); + break; + } + case OPCODE_LRP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//LRP\n"); +#endif + GLSLCallHelper3(psContext, "mix", psInst, 0, 2, 3, 1, 1); + break; + } + case OPCODE_DP2ADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//DP2ADD\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = dot(vec2("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, "), vec2("); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ")) + "); + TranslateOperand(psContext, &psInst->asOperands[3], TO_FLAG_NONE); + bcatcstr(glsl, ";\n"); + break; + } + case OPCODE_POW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//POW\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = pow(abs("); + TranslateOperand(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(glsl, "), "); + TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(glsl, ");\n"); + break; + } + + case OPCODE_IMM_ATOMIC_ALLOC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_ALLOC\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(glsl, " = int(atomicCounterIncrement("); + ResourceName(glsl, psContext, RGROUP_UAV, psInst->asOperands[1].ui32RegisterNumber, 0); + bformata(glsl, "_counter"); + bcatcstr(glsl, "));\n"); + break; + } + case OPCODE_IMM_ATOMIC_CONSUME: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//IMM_ATOMIC_CONSUME\n"); +#endif + AddIndentation(psContext); + TranslateOperand(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + // Temps are always signed and atomci counters are always unsigned + // at the moment. + bcatcstr(glsl, " = int(atomicCounterDecrement("); + ResourceName(glsl, psContext, RGROUP_UAV, psInst->asOperands[1].ui32RegisterNumber, 0); + bformata(glsl, "_counter"); + bcatcstr(glsl, "));\n"); + break; + } + + case OPCODE_NOT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//INOT\n"); +#endif + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_INT, GetNumSwizzleElements(&psInst->asOperands[1]), &numParenthesis); + + bcatcstr(glsl, "~"); + TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER, GetOperandWriteMask(&psInst->asOperands[0])); + GLSLAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//XOR\n"); +#endif + + GLSLCallBinaryOp(psContext, "^", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_RESINFO: + { + uint32_t destElemCount = GetNumSwizzleElements(&psInst->asOperands[0]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(glsl, "//RESINFO\n"); +#endif + + for (destElem = 0; destElem < destElemCount; ++destElem) + { + + GetResInfoData(psContext, psInst, psInst->asOperands[2].aui32Swizzle[destElem], destElem); + } + + break; + } + + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DMOV: + case OPCODE_DMOVC: + case OPCODE_DTOF: + case OPCODE_FTOD: + case OPCODE_DDIV: + case OPCODE_DFMA: + case OPCODE_DRCP: + case OPCODE_MSAD: + case OPCODE_DTOI: + case OPCODE_DTOU: + case OPCODE_ITOD: + case OPCODE_UTOD: + default: + { + ASSERT(0); + break; + } + } + + if (psInst->bSaturate) // Saturate is only for floating point data (float opcodes or MOV) + { + int dstCount = GetNumSwizzleElements(&psInst->asOperands[0]); + AddIndentation(psContext); + GLSLMETALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, dstCount, &numParenthesis); + bcatcstr(glsl, "clamp("); + + TranslateOperand(psContext, &psInst->asOperands[0], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(glsl, ", 0.0, 1.0)"); + GLSLAddAssignPrologue(psContext, numParenthesis); + } +} + +static int GLSLIsIntegerImmediateOpcode(OPCODE_TYPE eOpcode) +{ + switch (eOpcode) + { + case OPCODE_IADD: + case OPCODE_IF: + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INE: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_ITOF: + case OPCODE_USHR: + case OPCODE_AND: + case OPCODE_OR: + case OPCODE_XOR: + case OPCODE_BREAKC: + case OPCODE_CONTINUEC: + case OPCODE_RETC: + case OPCODE_DISCARD: + // MOV is typeless. + // Treat immediates as int, bitcast to float if necessary + case OPCODE_MOV: + case OPCODE_MOVC: + { + return 1; + } + default: + { + return 0; + } + } +} + +int InstructionUsesRegister(const Instruction* psInst, const Operand* psOperand) +{ + uint32_t operand; + for (operand = 0; operand < psInst->ui32NumOperands; ++operand) + { + if (psInst->asOperands[operand].eType == psOperand->eType) + { + if (psInst->asOperands[operand].ui32RegisterNumber == psOperand->ui32RegisterNumber) + { + if (CompareOperandSwizzles(&psInst->asOperands[operand], psOperand)) + { + return 1; + } + } + } + } + return 0; +} + +void MarkIntegerImmediates(HLSLCrossCompilerContext* psContext) +{ + const uint32_t count = psContext->psShader->asPhase[MAIN_PHASE].pui32InstCount[0]; + Instruction* psInst = psContext->psShader->asPhase[MAIN_PHASE].ppsInst[0]; + uint32_t i; + + for (i = 0; i < count;) + { + if (psInst[i].eOpcode == OPCODE_MOV && psInst[i].asOperands[1].eType == OPERAND_TYPE_IMMEDIATE32 && psInst[i].asOperands[0].eType == OPERAND_TYPE_TEMP) + { + uint32_t k; + + for (k = i + 1; k < count; ++k) + { + if (psInst[k].eOpcode == OPCODE_ILT) + { + k = k; + } + if (InstructionUsesRegister(&psInst[k], &psInst[i].asOperands[0])) + { + if (GLSLIsIntegerImmediateOpcode(psInst[k].eOpcode)) + { + psInst[i].asOperands[1].iIntegerImmediate = 1; + } + + goto next_iteration; + } + } + } + next_iteration: + ++i; + } +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLOperand.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLOperand.c new file mode 100644 index 0000000000..f6595ad2cf --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toGLSLOperand.c @@ -0,0 +1,1869 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toGLSLOperand.h" +#include "bstrlib.h" +#include "hlslcc.h" +#include "internal_includes/debug.h" +#include "internal_includes/toGLSLDeclaration.h" + +#include +#include + +#ifdef _MSC_VER +#define isnan(x) _isnan(x) +#define isinf(x) (!_finite(x)) +#endif + +#define fpcheck(x) (isnan(x) || isinf(x)) + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); + +uint32_t SVTTypeToFlag(const SHADER_VARIABLE_TYPE eType) +{ + if (eType == SVT_UINT) + { + return TO_FLAG_UNSIGNED_INTEGER; + } + else if (eType == SVT_INT) + { + return TO_FLAG_INTEGER; + } + else if (eType == SVT_BOOL) + { + return TO_FLAG_INTEGER; // TODO bools? + } + else + { + return TO_FLAG_NONE; + } +} + +SHADER_VARIABLE_TYPE TypeFlagsToSVTType(const uint32_t typeflags) +{ + if (typeflags & (TO_FLAG_INTEGER | TO_AUTO_BITCAST_TO_INT)) + return SVT_INT; + if (typeflags & (TO_FLAG_UNSIGNED_INTEGER | TO_AUTO_BITCAST_TO_UINT)) + return SVT_UINT; + return SVT_FLOAT; +} + +uint32_t GetOperandWriteMask(const Operand* psOperand) +{ + if (psOperand->eSelMode != OPERAND_4_COMPONENT_MASK_MODE || psOperand->ui32CompMask == 0) + return OPERAND_4_COMPONENT_MASK_ALL; + + return psOperand->ui32CompMask; +} + +const char* GetConstructorForType(const SHADER_VARIABLE_TYPE eType, const int components) +{ + static const char* const uintTypes[] = {" ", "uint", "uvec2", "uvec3", "uvec4"}; + static const char* const intTypes[] = {" ", "int", "ivec2", "ivec3", "ivec4"}; + static const char* const floatTypes[] = {" ", "float", "vec2", "vec3", "vec4"}; + + if (components < 1 || components > 4) + return "ERROR TOO MANY COMPONENTS IN VECTOR"; + + switch (eType) + { + case SVT_UINT: + return uintTypes[components]; + case SVT_INT: + return intTypes[components]; + case SVT_FLOAT: + return floatTypes[components]; + default: + return "ERROR UNSUPPORTED TYPE"; + } +} + +const char* GetConstructorForTypeFlag(const uint32_t ui32Flag, const int components) +{ + if (ui32Flag & TO_FLAG_UNSIGNED_INTEGER || ui32Flag & TO_AUTO_BITCAST_TO_UINT) + { + return GetConstructorForType(SVT_UINT, components); + } + else if (ui32Flag & TO_FLAG_INTEGER || ui32Flag & TO_AUTO_BITCAST_TO_INT) + { + return GetConstructorForType(SVT_INT, components); + } + else + { + return GetConstructorForType(SVT_FLOAT, components); + } +} + +int GetMaxComponentFromComponentMask(const Operand* psOperand) +{ + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents == 4) + { + // Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && + psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + return 4; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + return 3; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + return 2; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + return 1; + } + } + } + else + // Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + return 4; + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + return 1; + } + } + + return 4; +} + +// Single component repeated +// e..g .wwww +uint32_t IsSwizzleReplicated(const Operand* psOperand) +{ + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents == 4) + { + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == WWWW_SWIZZLE || psOperand->ui32Swizzle == ZZZZ_SWIZZLE || psOperand->ui32Swizzle == YYYY_SWIZZLE || + psOperand->ui32Swizzle == XXXX_SWIZZLE) + { + return 1; + } + } + } + return 0; +} + +static uint32_t GLSLGetNumberBitsSet(uint32_t a) +{ + // Calculate number of bits in a + // Taken from https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64 + // Works only up to 14 bits (we're only using up to 4) + return (a * 0x200040008001ULL & 0x111111111111111ULL) % 0xf; +} + +// e.g. +//.z = 1 +//.x = 1 +//.yw = 2 +uint32_t GetNumSwizzleElements(const Operand* psOperand) +{ + return GetNumSwizzleElementsWithMask(psOperand, OPERAND_4_COMPONENT_MASK_ALL); +} + +// Get the number of elements returned by operand, taking additional component mask into account +uint32_t GetNumSwizzleElementsWithMask(const Operand* psOperand, uint32_t ui32CompMask) +{ + uint32_t count = 0; + + switch (psOperand->eType) + { + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: + return 1; // TODO: does mask make any sense here? + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: + case OPERAND_TYPE_INPUT_THREAD_ID: + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: + // Adjust component count and break to more processing + ((Operand*)psOperand)->iNumComponents = 3; + break; + case OPERAND_TYPE_IMMEDIATE32: + case OPERAND_TYPE_IMMEDIATE64: + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH: + { + // Translate numComponents into bitmask + // 1 -> 1, 2 -> 3, 3 -> 7 and 4 -> 15 + uint32_t compMask = (1 << psOperand->iNumComponents) - 1; + + compMask &= ui32CompMask; + // Calculate bits left in compMask + return GLSLGetNumberBitsSet(compMask); + } + default: + { + break; + } + } + + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents != 1) + { + // Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t compMask = psOperand->ui32CompMask; + if (compMask == 0) + compMask = OPERAND_4_COMPONENT_MASK_ALL; + compMask &= ui32CompMask; + + if (compMask == OPERAND_4_COMPONENT_MASK_ALL) + return 4; + + if (compMask & OPERAND_4_COMPONENT_MASK_X) + { + count++; + } + if (compMask & OPERAND_4_COMPONENT_MASK_Y) + { + count++; + } + if (compMask & OPERAND_4_COMPONENT_MASK_Z) + { + count++; + } + if (compMask & OPERAND_4_COMPONENT_MASK_W) + { + count++; + } + } + else + // Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if ((ui32CompMask & (1 << i)) == 0) + continue; + + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + count++; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + count++; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + count++; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + count++; + } + } + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X && (ui32CompMask & OPERAND_4_COMPONENT_MASK_X)) + { + count++; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y && (ui32CompMask & OPERAND_4_COMPONENT_MASK_Y)) + { + count++; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z && (ui32CompMask & OPERAND_4_COMPONENT_MASK_Z)) + { + count++; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W && (ui32CompMask & OPERAND_4_COMPONENT_MASK_W)) + { + count++; + } + } + + // Component Select 1 + } + + if (!count) + { + // Translate numComponents into bitmask + // 1 -> 1, 2 -> 3, 3 -> 7 and 4 -> 15 + uint32_t compMask = (1 << psOperand->iNumComponents) - 1; + + compMask &= ui32CompMask; + // Calculate bits left in compMask + return GLSLGetNumberBitsSet(compMask); + } + + return count; +} + +void AddSwizzleUsingElementCount(HLSLCrossCompilerContext* psContext, uint32_t count) +{ + bstring glsl = *psContext->currentShaderString; + if (count == 4) + return; + if (count) + { + bcatcstr(glsl, "."); + bcatcstr(glsl, "x"); + count--; + } + if (count) + { + bcatcstr(glsl, "y"); + count--; + } + if (count) + { + bcatcstr(glsl, "z"); + count--; + } + if (count) + { + bcatcstr(glsl, "w"); + count--; + } +} + +static uint32_t GLSLConvertOperandSwizzleToComponentMask(const Operand* psOperand) +{ + uint32_t mask = 0; + + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents == 4) + { + // Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + mask = psOperand->ui32CompMask; + } + else + // Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + mask |= OPERAND_4_COMPONENT_MASK_X; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + mask |= OPERAND_4_COMPONENT_MASK_Y; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + mask |= OPERAND_4_COMPONENT_MASK_Z; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + mask |= OPERAND_4_COMPONENT_MASK_W; + } + } + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + mask |= OPERAND_4_COMPONENT_MASK_X; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + mask |= OPERAND_4_COMPONENT_MASK_Y; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + mask |= OPERAND_4_COMPONENT_MASK_Z; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + mask |= OPERAND_4_COMPONENT_MASK_W; + } + } + + // Component Select 1 + } + + return mask; +} + +// Non-zero means the components overlap +int CompareOperandSwizzles(const Operand* psOperandA, const Operand* psOperandB) +{ + uint32_t maskA = GLSLConvertOperandSwizzleToComponentMask(psOperandA); + uint32_t maskB = GLSLConvertOperandSwizzleToComponentMask(psOperandB); + + return maskA & maskB; +} + +void TranslateOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + TranslateOperandSwizzleWithMask(psContext, psOperand, OPERAND_4_COMPONENT_MASK_ALL); +} + +void TranslateOperandSwizzleWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask) +{ + bstring glsl = *psContext->currentShaderString; + + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return; + } + } + + if (psOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + /*ConstantBuffer* psCBuf = NULL; + ShaderVar* psVar = NULL; + int32_t index = -1; + GetConstantBufferFromBindingPoint(psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + + //Access the Nth vec4 (N=psOperand->aui32ArraySizes[1]) + //then apply the sizzle. + + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVar, &index); + + bformata(glsl, ".%s", psVar->Name); + if(index != -1) + { + bformata(glsl, "[%d]", index); + }*/ + + // return; + } + + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents != 1) + { + // Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t mask; + if (psOperand->ui32CompMask != 0) + mask = psOperand->ui32CompMask & ui32ComponentMask; + else + mask = ui32ComponentMask; + + if (mask != 0 && mask != OPERAND_4_COMPONENT_MASK_ALL) + { + bcatcstr(glsl, "."); + if (mask & OPERAND_4_COMPONENT_MASK_X) + { + bcatcstr(glsl, "x"); + } + if (mask & OPERAND_4_COMPONENT_MASK_Y) + { + bcatcstr(glsl, "y"); + } + if (mask & OPERAND_4_COMPONENT_MASK_Z) + { + bcatcstr(glsl, "z"); + } + if (mask & OPERAND_4_COMPONENT_MASK_W) + { + bcatcstr(glsl, "w"); + } + } + } + else + // Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (ui32ComponentMask != OPERAND_4_COMPONENT_MASK_ALL || + !(psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X && psOperand->aui32Swizzle[1] == OPERAND_4_COMPONENT_Y && + psOperand->aui32Swizzle[2] == OPERAND_4_COMPONENT_Z && psOperand->aui32Swizzle[3] == OPERAND_4_COMPONENT_W)) + { + uint32_t i; + + bcatcstr(glsl, "."); + + for (i = 0; i < 4; ++i) + { + if (!(ui32ComponentMask & (OPERAND_4_COMPONENT_MASK_X << i))) + continue; + + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + bcatcstr(glsl, "x"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + bcatcstr(glsl, "y"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + bcatcstr(glsl, "z"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + bcatcstr(glsl, "w"); + } + } + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) // ui32ComponentMask is ignored in this case + { + bcatcstr(glsl, "."); + + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + bcatcstr(glsl, "x"); + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + bcatcstr(glsl, "y"); + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + bcatcstr(glsl, "z"); + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + bcatcstr(glsl, "w"); + } + } + + // Component Select 1 + } +} + +int GetFirstOperandSwizzle(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return -1; + } + } + + if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents == 4) + { + // Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && + psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + return 0; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + return 1; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + return 2; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + return 3; + } + } + } + else + // Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + return 0; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + return 1; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + return 2; + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + return 3; + } + } + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + return 0; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + return 1; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + return 2; + } + else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + return 3; + } + } + + // Component Select 1 + } + + return -1; +} + +void TranslateOperandIndex(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index) +{ + int i = index; + int isGeoShader = psContext->psShader->eShaderType == GEOMETRY_SHADER ? 1 : 0; + + bstring glsl = *psContext->currentShaderString; + + ASSERT(index < psOperand->iIndexDims); + + switch (psOperand->eIndexRep[i]) + { + case OPERAND_INDEX_IMMEDIATE32: + { + if (i > 0 || isGeoShader) + { + bformata(glsl, "[%d]", psOperand->aui32ArraySizes[i]); + } + else + { + bformata(glsl, "%d", psOperand->aui32ArraySizes[i]); + } + break; + } + case OPERAND_INDEX_RELATIVE: + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + bcatcstr(glsl, "["); // Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bformata(glsl, " + %d]", psOperand->aui32ArraySizes[i]); + break; + } + default: + { + break; + } + } +} + +void TranslateOperandIndexMAD(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add) +{ + int i = index; + int isGeoShader = psContext->psShader->eShaderType == GEOMETRY_SHADER ? 1 : 0; + + bstring glsl = *psContext->currentShaderString; + + ASSERT(index < psOperand->iIndexDims); + + switch (psOperand->eIndexRep[i]) + { + case OPERAND_INDEX_IMMEDIATE32: + { + if (i > 0 || isGeoShader) + { + bformata(glsl, "[%d*%d+%d]", psOperand->aui32ArraySizes[i], multiply, add); + } + else + { + bformata(glsl, "%d*%d+%d", psOperand->aui32ArraySizes[i], multiply, add); + } + break; + } + case OPERAND_INDEX_RELATIVE: + { + bcatcstr(glsl, "[int("); // Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_NONE); + bformata(glsl, ")*%d+%d]", multiply, add); + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + bcatcstr(glsl, "[(int("); // Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[i], TO_FLAG_NONE); + bformata(glsl, ") + %d)*%d+%d]", psOperand->aui32ArraySizes[i], multiply, add); + break; + } + default: + { + break; + } + } +} + +// Returns nonzero if a direct constructor can convert src->dest +static int GLSLCanDoDirectCast(HLSLCrossCompilerContext* psContext, SHADER_VARIABLE_TYPE src, SHADER_VARIABLE_TYPE dest) +{ + // Only option on pre-SM4 stuff + if (psContext->psShader->ui32MajorVersion < 4) + return 1; + + // uint<->int<->bool conversions possible + if ((src == SVT_INT || src == SVT_UINT || src == SVT_BOOL) && (dest == SVT_INT || dest == SVT_UINT || dest == SVT_BOOL)) + return 1; + + // float<->double possible + if ((src == SVT_FLOAT || src == SVT_DOUBLE) && (dest == SVT_FLOAT || dest == SVT_DOUBLE)) + return 1; + + return 0; +} + +static const char* GetBitcastOp(SHADER_VARIABLE_TYPE from, SHADER_VARIABLE_TYPE to) +{ + if (to == SVT_FLOAT && from == SVT_INT) + return "intBitsToFloat"; + else if (to == SVT_FLOAT && from == SVT_UINT) + return "uintBitsToFloat"; + else if (to == SVT_INT && from == SVT_FLOAT) + return "floatBitsToInt"; + else if (to == SVT_UINT && from == SVT_FLOAT) + return "floatBitsToUint"; + + return "ERROR missing components in GetBitcastOp()"; +} + +// Helper function to print out a single 32-bit immediate value in desired format +static void GLSLprintImmediate32(HLSLCrossCompilerContext* psContext, uint32_t value, SHADER_VARIABLE_TYPE eType) +{ + bstring glsl = *psContext->currentShaderString; + int needsParenthesis = 0; + + // Print floats as bit patterns. + if (eType == SVT_FLOAT && psContext->psShader->ui32MajorVersion > 3) + { + bcatcstr(glsl, "intBitsToFloat("); + eType = SVT_INT; + needsParenthesis = 1; + } + + switch (eType) + { + default: + case SVT_INT: + // Need special handling for anything >= uint 0x3fffffff + if (value > 0x3ffffffe) + bformata(glsl, "int(0x%Xu)", value); + else + bformata(glsl, "0x%X", value); + break; + case SVT_UINT: + bformata(glsl, "%uu", value); + break; + case SVT_FLOAT: + bformata(glsl, "%f", *((float*)(&value))); + break; + } + if (needsParenthesis) + bcatcstr(glsl, ")"); +} + +static void GLSLGLSLTranslateVariableNameWithMask(HLSLCrossCompilerContext* psContext, + const Operand* psOperand, + uint32_t ui32TOFlag, + uint32_t* pui32IgnoreSwizzle, + uint32_t ui32CompMask) +{ + int numParenthesis = 0; + int hasCtor = 0; + bstring glsl = *psContext->currentShaderString; + SHADER_VARIABLE_TYPE requestedType = TypeFlagsToSVTType(ui32TOFlag); + SHADER_VARIABLE_TYPE eType = GetOperandDataTypeEx(psContext, psOperand, requestedType); + int numComponents = GetNumSwizzleElementsWithMask(psOperand, ui32CompMask); + int requestedComponents = 0; + + if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC2) + requestedComponents = 2; + else if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC3) + requestedComponents = 3; + else if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC4) + requestedComponents = 4; + + requestedComponents = max(requestedComponents, numComponents); + + *pui32IgnoreSwizzle = 0; + + if (!(ui32TOFlag & (TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY | TO_FLAG_DECLARATION_NAME))) + { + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32 || psOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + // Mark the operand type to match whatever we're asking for in the flags. + ((Operand*)psOperand)->aeDataType[0] = requestedType; + ((Operand*)psOperand)->aeDataType[1] = requestedType; + ((Operand*)psOperand)->aeDataType[2] = requestedType; + ((Operand*)psOperand)->aeDataType[3] = requestedType; + } + + if (eType != requestedType) + { + if (GLSLCanDoDirectCast(psContext, eType, requestedType)) + { + bformata(glsl, "%s(", GetConstructorForType(requestedType, requestedComponents)); + numParenthesis++; + hasCtor = 1; + } + else + { + // Direct cast not possible, need to do bitcast. + bformata(glsl, "%s(", GetBitcastOp(eType, requestedType)); + numParenthesis++; + } + } + + // Add ctor if needed (upscaling) + if (numComponents < requestedComponents && (hasCtor == 0)) + { + ASSERT(numComponents == 1); + bformata(glsl, "%s(", GetConstructorForType(requestedType, requestedComponents)); + numParenthesis++; + hasCtor = 1; + } + } + + switch (psOperand->eType) + { + case OPERAND_TYPE_IMMEDIATE32: + { + if (psOperand->iNumComponents == 1) + { + GLSLprintImmediate32(psContext, *((unsigned int*)(&psOperand->afImmediates[0])), requestedType); + } + else + { + int i; + int firstItemAdded = 0; + if (hasCtor == 0) + { + bformata(glsl, "%s(", GetConstructorForType(requestedType, numComponents)); + numParenthesis++; + hasCtor = 1; + } + for (i = 0; i < 4; i++) + { + uint32_t uval; + if (!(ui32CompMask & (1 << i))) + continue; + + if (firstItemAdded) + bcatcstr(glsl, ", "); + uval = *((uint32_t*)(&psOperand->afImmediates[i])); + GLSLprintImmediate32(psContext, uval, requestedType); + firstItemAdded = 1; + } + bcatcstr(glsl, ")"); + *pui32IgnoreSwizzle = 1; + numParenthesis--; + } + break; + } + case OPERAND_TYPE_IMMEDIATE64: + { + if (psOperand->iNumComponents == 1) + { + bformata(glsl, "%f", psOperand->adImmediates[0]); + } + else + { + bformata(glsl, "dvec4(%f, %f, %f, %f)", psOperand->adImmediates[0], psOperand->adImmediates[1], psOperand->adImmediates[2], + psOperand->adImmediates[3]); + if (psOperand->iNumComponents != 4) + { + AddSwizzleUsingElementCount(psContext, psOperand->iNumComponents); + } + } + break; + } + case OPERAND_TYPE_INPUT: + { + switch (psOperand->iIndexDims) + { + case INDEX_2D: + { + if (psOperand->aui32ArraySizes[1] == 0) // Input index zero - position. + { + bcatcstr(glsl, "gl_in"); + TranslateOperandIndex(psContext, psOperand, 0); // Vertex index + bcatcstr(glsl, ".gl_Position"); + } + else + { + const char* name = "Input"; + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + name = GetDeclaredInputName(psContext, psContext->psShader->eShaderType, psOperand); + } + + bformata(glsl, "%s%d", name, psOperand->aui32ArraySizes[1]); + TranslateOperandIndex(psContext, psOperand, 0); // Vertex index + } + break; + } + default: + { + if (psOperand->eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE) + { + bformata(glsl, "Input%d[", psOperand->ui32RegisterNumber); + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + } + else + { + if (psContext->psShader->aIndexedInput[psOperand->ui32RegisterNumber] != 0) + { + const uint32_t parentIndex = psContext->psShader->aIndexedInputParents[psOperand->ui32RegisterNumber]; + bformata(glsl, "Input%d[%d]", parentIndex, psOperand->ui32RegisterNumber - parentIndex); + } + else + { + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + const char* name = GetDeclaredInputName(psContext, psContext->psShader->eShaderType, psOperand); + bcatcstr(glsl, name); + } + else + { + bformata(glsl, "Input%d", psOperand->ui32RegisterNumber); + } + } + } + break; + } + } + break; + } + case OPERAND_TYPE_OUTPUT: + { + bformata(glsl, "Output%d", psOperand->ui32RegisterNumber); + if (psOperand->psSubOperand[0]) + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_AUTO_BITCAST_TO_INT); + bcatcstr(glsl, "]"); + } + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH: + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + { + bcatcstr(glsl, "gl_FragDepth"); + break; + } + case OPERAND_TYPE_TEMP: + { + SHADER_VARIABLE_TYPE eType2 = GetOperandDataType(psContext, psOperand); + bcatcstr(glsl, "Temp"); + + if (eType2 == SVT_INT) + { + bcatcstr(glsl, "_int"); + } + else if (eType2 == SVT_UINT) + { + bcatcstr(glsl, "_uint"); + } + else if (eType2 == SVT_DOUBLE) + { + bcatcstr(glsl, "_double"); + } + else if (eType2 == SVT_VOID && (ui32TOFlag & TO_FLAG_DESTINATION)) + { + ASSERT(0 && "Should never get here!"); + /* if(ui32TOFlag & TO_FLAG_INTEGER) + { + bcatcstr(glsl, "_int"); + } + else + if(ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) + { + bcatcstr(glsl, "_uint"); + }*/ + } + + bformata(glsl, "[%d]", psOperand->ui32RegisterNumber); + + break; + } + case OPERAND_TYPE_SPECIAL_IMMCONSTINT: + { + bformata(glsl, "IntImmConst%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_SPECIAL_IMMCONST: + { + if (psOperand->psSubOperand[0] != NULL) + { + if (psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber] != 0) + bformata(glsl, "ImmConstArray[%d + ", psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber]); + else + bcatcstr(glsl, "ImmConstArray["); + TranslateOperandWithMask(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(glsl, "]"); + } + else + { + bformata(glsl, "ImmConst%d", psOperand->ui32RegisterNumber); + } + break; + } + case OPERAND_TYPE_SPECIAL_OUTBASECOLOUR: + { + bcatcstr(glsl, "BaseColour"); + break; + } + case OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR: + { + bcatcstr(glsl, "OffsetColour"); + break; + } + case OPERAND_TYPE_SPECIAL_POSITION: + { + bcatcstr(glsl, "gl_Position"); + break; + } + case OPERAND_TYPE_SPECIAL_FOG: + { + bcatcstr(glsl, "Fog"); + break; + } + case OPERAND_TYPE_SPECIAL_POINTSIZE: + { + bcatcstr(glsl, "gl_PointSize"); + break; + } + case OPERAND_TYPE_SPECIAL_ADDRESS: + { + bcatcstr(glsl, "Address"); + break; + } + case OPERAND_TYPE_SPECIAL_LOOPCOUNTER: + { + bcatcstr(glsl, "LoopCounter"); + pui32IgnoreSwizzle[0] = 1; + break; + } + case OPERAND_TYPE_SPECIAL_TEXCOORD: + { + bformata(glsl, "TexCoord%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_CONSTANT_BUFFER: + { + const char* StageName = "VS"; + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + StageName = "PS"; + break; + } + case HULL_SHADER: + { + StageName = "HS"; + break; + } + case DOMAIN_SHADER: + { + StageName = "DS"; + break; + } + case GEOMETRY_SHADER: + { + StageName = "GS"; + break; + } + case COMPUTE_SHADER: + { + StageName = "CS"; + break; + } + default: + { + break; + } + } + + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + pui32IgnoreSwizzle[0] = 1; + } + + // FIXME: With ES 3.0 the buffer name is often not prepended to variable names + if (((psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) != HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) && + ((psContext->flags & HLSLCC_FLAG_DISABLE_GLOBALS_STRUCT) != HLSLCC_FLAG_DISABLE_GLOBALS_STRUCT)) + { + if (psCBuf) + { + //$Globals. + if (psCBuf->Name[0] == '$') + { + bformata(glsl, "Globals%s", StageName); + } + else + { + bformata(glsl, "%s%s", psCBuf->Name, StageName); + } + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(glsl, "."); + } + } + else + { + // bformata(glsl, "cb%d", psOperand->aui32ArraySizes[0]); + } + } + + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + // Work out the variable name. Don't apply swizzle to that variable yet. + int32_t rebase = 0; + + if (psCBuf && !psCBuf->blob) + { + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + + bformata(glsl, "%s", psVarType->FullName); + } + else if (psCBuf) + { + bformata(glsl, "%s%s_data", psCBuf->Name, StageName); + index = psOperand->aui32ArraySizes[1]; + } + else // We don't have a semantic for this variable, so try the raw dump appoach. + { + bformata(glsl, "cb%d.data", psOperand->aui32ArraySizes[0]); // + index = psOperand->aui32ArraySizes[1]; + } + + // Dx9 only? + if (psOperand->psSubOperand[0] != NULL) + { + // Array of matrices is treated as array of vec4s in HLSL, + // but that would mess up uniform types in GLSL. Do gymnastics. + uint32_t opFlags = TO_FLAG_INTEGER; + + if (psVarType && (psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) && (psVarType->Elements > 1)) + { + // Special handling for matrix arrays + bcatcstr(glsl, "[("); + TranslateOperand(psContext, psOperand->psSubOperand[0], opFlags); + bformata(glsl, ") / 4]"); + if (psContext->psShader->eTargetLanguage <= LANG_120) + { + bcatcstr(glsl, "[int(mod(float("); + TranslateOperandWithMask(psContext, psOperand->psSubOperand[0], opFlags, OPERAND_4_COMPONENT_MASK_X); + bformata(glsl, "), 4.0))]"); + } + else + { + bcatcstr(glsl, "[(("); + TranslateOperandWithMask(psContext, psOperand->psSubOperand[0], opFlags, OPERAND_4_COMPONENT_MASK_X); + bformata(glsl, ") %% 4)]"); + } + } + else + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[0], opFlags); + bformata(glsl, "]"); + } + } + else if (index != -1 && psOperand->psSubOperand[1] != NULL) + { + // Array of matrices is treated as array of vec4s in HLSL, + // but that would mess up uniform types in GLSL. Do gymnastics. + SHADER_VARIABLE_TYPE eType2 = GetOperandDataType(psContext, psOperand->psSubOperand[1]); + uint32_t opFlags = TO_FLAG_INTEGER; + if (eType2 != SVT_INT && eType2 != SVT_UINT) + opFlags = TO_AUTO_BITCAST_TO_INT; + + if (psVarType && (psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) && (psVarType->Elements > 1)) + { + // Special handling for matrix arrays + bcatcstr(glsl, "[("); + TranslateOperand(psContext, psOperand->psSubOperand[1], opFlags); + bformata(glsl, " + %d) / 4]", index); + if (psContext->psShader->eTargetLanguage <= LANG_120) + { + bcatcstr(glsl, "[int(mod(float("); + TranslateOperand(psContext, psOperand->psSubOperand[1], opFlags); + bformata(glsl, " + %d), 4.0))]", index); + } + else + { + bcatcstr(glsl, "[(("); + TranslateOperand(psContext, psOperand->psSubOperand[1], opFlags); + bformata(glsl, " + %d) %% 4)]", index); + } + } + else + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[1], opFlags); + bformata(glsl, " + %d]", index); + } + } + else if (index != -1) + { + if ((psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) && (psVarType->Elements > 1)) + { + // Special handling for matrix arrays, open them up into vec4's + size_t matidx = index / 4; + size_t rowidx = index - (matidx * 4); + bformata(glsl, "[%d][%d]", matidx, rowidx); + } + else + { + bformata(glsl, "[%d]", index); + } + } + else if (psOperand->psSubOperand[1] != NULL) + { + bcatcstr(glsl, "["); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + bcatcstr(glsl, "]"); + } + + if (psVarType && psVarType->Class == SVC_VECTOR) + { + switch (rebase) + { + case 4: + { + if (psVarType->Columns == 2) + { + //.x(GLSL) is .y(HLSL). .y(GLSL) is .z(HLSL) + bcatcstr(glsl, ".xxyx"); + } + else if (psVarType->Columns == 3) + { + //.x(GLSL) is .y(HLSL). .y(GLSL) is .z(HLSL) .z(GLSL) is .w(HLSL) + bcatcstr(glsl, ".xxyz"); + } + break; + } + case 8: + { + if (psVarType->Columns == 2) + { + //.x(GLSL) is .z(HLSL). .y(GLSL) is .w(HLSL) + bcatcstr(glsl, ".xxxy"); + } + break; + } + case 0: + default: + { + // No rebase, but extend to vec4. + if (psVarType->Columns == 2) + { + bcatcstr(glsl, ".xyxx"); + } + else if (psVarType->Columns == 3) + { + bcatcstr(glsl, ".xyzx"); + } + break; + } + } + } + + if (psVarType && psVarType->Class == SVC_SCALAR) + { + *pui32IgnoreSwizzle = 1; + } + } + break; + } + case OPERAND_TYPE_RESOURCE: + { + ResourceName(glsl, psContext, RGROUP_TEXTURE, psOperand->ui32RegisterNumber, 0); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_SAMPLER: + { + bformata(glsl, "Sampler%d", psOperand->ui32RegisterNumber); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_FUNCTION_BODY: + { + const uint32_t ui32FuncBody = psOperand->ui32RegisterNumber; + const uint32_t ui32FuncTable = psContext->psShader->aui32FuncBodyToFuncTable[ui32FuncBody]; + // const uint32_t ui32FuncPointer = psContext->psShader->aui32FuncTableToFuncPointer[ui32FuncTable]; + const uint32_t ui32ClassType = psContext->psShader->sInfo.aui32TableIDToTypeID[ui32FuncTable]; + const char* ClassTypeName = &psContext->psShader->sInfo.psClassTypes[ui32ClassType].Name[0]; + const uint32_t ui32UniqueClassFuncIndex = psContext->psShader->ui32NextClassFuncName[ui32ClassType]++; + + bformata(glsl, "%s_Func%d", ClassTypeName, ui32UniqueClassFuncIndex); + break; + } + case OPERAND_TYPE_INPUT_FORK_INSTANCE_ID: + { + bcatcstr(glsl, "forkInstanceID"); + *pui32IgnoreSwizzle = 1; + return; + } + case OPERAND_TYPE_IMMEDIATE_CONSTANT_BUFFER: + { + bcatcstr(glsl, "immediateConstBufferF"); + + if (psOperand->psSubOperand[0]) + { + bcatcstr(glsl, "("); // Indexes must be integral. + TranslateOperand(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(glsl, ")"); + } + break; + } + case OPERAND_TYPE_INPUT_DOMAIN_POINT: + { + bcatcstr(glsl, "gl_TessCoord"); + break; + } + case OPERAND_TYPE_INPUT_CONTROL_POINT: + { + if (psOperand->aui32ArraySizes[1] == 0) // Input index zero - position. + { + bformata(glsl, "gl_in[%d].gl_Position", psOperand->aui32ArraySizes[0]); + } + else + { + bformata(glsl, "Input%d[%d]", psOperand->aui32ArraySizes[1], psOperand->aui32ArraySizes[0]); + } + break; + } + case OPERAND_TYPE_NULL: + { + // Null register, used to discard results of operations + bcatcstr(glsl, "//null"); + break; + } + case OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID: + { + bcatcstr(glsl, "gl_InvocationID"); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + { + bcatcstr(glsl, "gl_SampleMask[0]"); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_COVERAGE_MASK: + { + bcatcstr(glsl, "gl_SampleMaskIn[0]"); + // Skip swizzle on scalar types. + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID: // SV_DispatchThreadID + { + bcatcstr(glsl, "gl_GlobalInvocationID"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: // SV_GroupThreadID + { + bcatcstr(glsl, "gl_LocalInvocationID"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: // SV_GroupID + { + bcatcstr(glsl, "gl_WorkGroupID"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: // SV_GroupIndex + { + bcatcstr(glsl, "gl_LocalInvocationIndex"); + *pui32IgnoreSwizzle = 1; // No swizzle meaningful for scalar. + break; + } + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + { + ResourceName(glsl, psContext, RGROUP_UAV, psOperand->ui32RegisterNumber, 0); + break; + } + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + bformata(glsl, "TGSM%d", psOperand->ui32RegisterNumber); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_PRIMITIVEID: + { + bcatcstr(glsl, "gl_PrimitiveID"); + break; + } + case OPERAND_TYPE_INDEXABLE_TEMP: + { + bformata(glsl, "TempArray%d", psOperand->aui32ArraySizes[0]); + bcatcstr(glsl, "["); + if (psOperand->aui32ArraySizes[1] != 0 || !psOperand->psSubOperand[1]) + bformata(glsl, "%d", psOperand->aui32ArraySizes[1]); + + if (psOperand->psSubOperand[1]) + { + if (psOperand->aui32ArraySizes[1] != 0) + bcatcstr(glsl, "+"); + TranslateOperand(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + } + bcatcstr(glsl, "]"); + break; + } + case OPERAND_TYPE_STREAM: + { + bformata(glsl, "%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_INPUT_GS_INSTANCE_ID: + { + // In HLSL the instance id is uint, so cast here. + bcatcstr(glsl, "uint(gl_InvocationID)"); + break; + } + case OPERAND_TYPE_THIS_POINTER: + { + /* + The "this" register is a register that provides up to 4 pieces of information: + X: Which CB holds the instance data + Y: Base element offset of the instance data within the instance CB + Z: Base sampler index + W: Base Texture index + + Can be different for each function call + */ + break; + } + case OPERAND_TYPE_INPUT_PATCH_CONSTANT: + { + bformata(glsl, "myPatchConst%d", psOperand->ui32RegisterNumber); + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (hasCtor && (*pui32IgnoreSwizzle == 0)) + { + TranslateOperandSwizzleWithMask(psContext, psOperand, ui32CompMask); + *pui32IgnoreSwizzle = 1; + } + + while (numParenthesis != 0) + { + bcatcstr(glsl, ")"); + numParenthesis--; + } +} + +static void GLSLTranslateVariableName(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle) +{ + GLSLGLSLTranslateVariableNameWithMask(psContext, psOperand, ui32TOFlag, pui32IgnoreSwizzle, OPERAND_4_COMPONENT_MASK_ALL); +} + +SHADER_VARIABLE_TYPE GetOperandDataType(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + return GetOperandDataTypeEx(psContext, psOperand, SVT_INT); +} + +SHADER_VARIABLE_TYPE GetOperandDataTypeEx(HLSLCrossCompilerContext* psContext, const Operand* psOperand, SHADER_VARIABLE_TYPE ePreferredTypeForImmediates) +{ + switch (psOperand->eType) + { + case OPERAND_TYPE_TEMP: + { + SHADER_VARIABLE_TYPE eCurrentType = SVT_VOID; + int i = 0; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + return psOperand->aeDataType[psOperand->aui32Swizzle[0]]; + } + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + return psOperand->aeDataType[0]; + } + + return psOperand->aeDataType[psOperand->aui32Swizzle[0]]; + } + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + for (; i < 4; ++i) + { + if (ui32CompMask & (1 << i)) + { + eCurrentType = psOperand->aeDataType[i]; + break; + } + } + +#ifdef _DEBUG + // Check if all elements have the same basic type. + for (; i < 4; ++i) + { + if (psOperand->ui32CompMask & (1 << i)) + { + if (eCurrentType != psOperand->aeDataType[i]) + { + ASSERT(0); + } + } + } +#endif + return eCurrentType; + } + + ASSERT(0); + + break; + } + case OPERAND_TYPE_OUTPUT: + { + const uint32_t ui32Register = psOperand->aui32ArraySizes[psOperand->iIndexDims - 1]; + InOutSignature* psOut; + + if (GetOutputSignatureFromRegister(psContext->currentPhase, ui32Register, psOperand->ui32CompMask, 0, &psContext->psShader->sInfo, &psOut)) + { + if (psOut->eComponentType == INOUT_COMPONENT_UINT32) + { + return SVT_UINT; + } + else if (psOut->eComponentType == INOUT_COMPONENT_SINT32) + { + return SVT_INT; + } + } + break; + } + case OPERAND_TYPE_INPUT: + { + const uint32_t ui32Register = psOperand->aui32ArraySizes[psOperand->iIndexDims - 1]; + InOutSignature* psIn; + + // UINT in DX, INT in GL. + if (psOperand->eSpecialName == NAME_PRIMITIVE_ID) + { + return SVT_INT; + } + + if (GetInputSignatureFromRegister(ui32Register, &psContext->psShader->sInfo, &psIn)) + { + if (psIn->eComponentType == INOUT_COMPONENT_UINT32) + { + return SVT_UINT; + } + else if (psIn->eComponentType == INOUT_COMPONENT_SINT32) + { + return SVT_INT; + } + } + break; + } + case OPERAND_TYPE_CONSTANT_BUFFER: + { + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + int32_t rebase = -1; + int foundVar; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + if (psCBuf && !psCBuf->blob) + { + foundVar = GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + if (foundVar && index == -1 && psOperand->psSubOperand[1] == NULL) + { + return psVarType->Type; + } + } + else + { + // Todo: this isn't correct yet. + return SVT_FLOAT; + } + break; + } + case OPERAND_TYPE_IMMEDIATE32: + { + return ePreferredTypeForImmediates; + } + + case OPERAND_TYPE_INPUT_THREAD_ID: + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: + { + return SVT_UINT; + } + case OPERAND_TYPE_SPECIAL_ADDRESS: + case OPERAND_TYPE_SPECIAL_LOOPCOUNTER: + { + return SVT_INT; + } + case OPERAND_TYPE_INPUT_GS_INSTANCE_ID: + { + return SVT_UINT; + } + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + { + return SVT_INT; + } + case OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID: + { + return SVT_INT; + } + default: + { + return SVT_FLOAT; + } + } + + return SVT_FLOAT; +} + +void TranslateOperand(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag) +{ + TranslateOperandWithMask(psContext, psOperand, ui32TOFlag, OPERAND_4_COMPONENT_MASK_ALL); +} + +void TranslateOperandWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t ui32ComponentMask) +{ + bstring glsl = *psContext->currentShaderString; + uint32_t ui32IgnoreSwizzle = 0; + + if (psContext->psShader->ui32MajorVersion <= 3) + { + ui32TOFlag &= ~(TO_AUTO_BITCAST_TO_FLOAT | TO_AUTO_BITCAST_TO_INT | TO_AUTO_BITCAST_TO_UINT); + } + + if (ui32TOFlag & TO_FLAG_NAME_ONLY) + { + GLSLTranslateVariableName(psContext, psOperand, ui32TOFlag, &ui32IgnoreSwizzle); + return; + } + + switch (psOperand->eModifier) + { + case OPERAND_MODIFIER_NONE: + { + break; + } + case OPERAND_MODIFIER_NEG: + { + bcatcstr(glsl, "(-"); + break; + } + case OPERAND_MODIFIER_ABS: + { + bcatcstr(glsl, "abs("); + break; + } + case OPERAND_MODIFIER_ABSNEG: + { + bcatcstr(glsl, "-abs("); + break; + } + } + + GLSLGLSLTranslateVariableNameWithMask(psContext, psOperand, ui32TOFlag, &ui32IgnoreSwizzle, ui32ComponentMask); + + if (!ui32IgnoreSwizzle) + { + TranslateOperandSwizzleWithMask(psContext, psOperand, ui32ComponentMask); + } + + switch (psOperand->eModifier) + { + case OPERAND_MODIFIER_NONE: + { + break; + } + case OPERAND_MODIFIER_NEG: + { + bcatcstr(glsl, ")"); + break; + } + case OPERAND_MODIFIER_ABS: + { + bcatcstr(glsl, ")"); + break; + } + case OPERAND_MODIFIER_ABSNEG: + { + bcatcstr(glsl, ")"); + break; + } + } +} + +void ResourceName(bstring targetStr, HLSLCrossCompilerContext* psContext, ResourceGroup group, const uint32_t ui32RegisterNumber, const int bZCompare) +{ + bstring glsl = (targetStr == NULL) ? *psContext->currentShaderString : targetStr; + ResourceBinding* psBinding = 0; + int found; + + found = GetResourceFromBindingPoint(group, ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + + if (bZCompare) + { + bcatcstr(glsl, "hlslcc_zcmp"); + } + + if (found) + { + int i = 0; + char name[MAX_REFLECT_STRING_LENGTH]; + uint32_t ui32ArrayOffset = ui32RegisterNumber - psBinding->ui32BindPoint; + + while (psBinding->Name[i] != '\0' && i < (MAX_REFLECT_STRING_LENGTH - 1)) + { + name[i] = psBinding->Name[i]; + + // array syntax [X] becomes _0_ + // Otherwise declarations could end up as: + // uniform sampler2D SomeTextures[0]; + // uniform sampler2D SomeTextures[1]; + if (name[i] == '[' || name[i] == ']') + name[i] = '_'; + + ++i; + } + + name[i] = '\0'; + + if (ui32ArrayOffset) + { + bformata(glsl, "%s%d", name, ui32ArrayOffset); + } + else + { + bformata(glsl, "%s", name); + } + } + else + { + bformata(glsl, "UnknownResource%d", ui32RegisterNumber); + } +} + +bstring TextureSamplerName(ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare) +{ + bstring result; + ResourceBinding* psTextureBinding = 0; + ResourceBinding* psSamplerBinding = 0; + int foundTexture, foundSampler; + uint32_t i = 0; + char textureName[MAX_REFLECT_STRING_LENGTH]; + uint32_t ui32ArrayOffset; + + foundTexture = GetResourceFromBindingPoint(RGROUP_TEXTURE, ui32TextureRegisterNumber, psShaderInfo, &psTextureBinding); + foundSampler = GetResourceFromBindingPoint(RGROUP_SAMPLER, ui32SamplerRegisterNumber, psShaderInfo, &psSamplerBinding); + + if (!foundTexture || !foundSampler) + { + result = bformat("UnknownResource%d_%d", ui32TextureRegisterNumber, ui32SamplerRegisterNumber); + return result; + } + + ui32ArrayOffset = ui32TextureRegisterNumber - psTextureBinding->ui32BindPoint; + + while (psTextureBinding->Name[i] != '\0' && i < (MAX_REFLECT_STRING_LENGTH - 1)) + { + textureName[i] = psTextureBinding->Name[i]; + + // array syntax [X] becomes _0_ + // Otherwise declarations could end up as: + // uniform sampler2D SomeTextures[0]; + // uniform sampler2D SomeTextures[1]; + if (textureName[i] == '[' || textureName[i] == ']') + { + textureName[i] = '_'; + } + + ++i; + } + textureName[i] = '\0'; + + result = bfromcstr(""); + + if (bZCompare) + { + bcatcstr(result, "hlslcc_zcmp"); + } + + if (ui32ArrayOffset) + { + bformata(result, "%s%d_X_%s", textureName, ui32ArrayOffset, psSamplerBinding->Name); + } + else + { + if ((i > 0) && (textureName[i - 1] == '_')) // Prevent double underscore which is reserved + { + bformata(result, "%sX_%s", textureName, psSamplerBinding->Name); + } + else + { + bformata(result, "%s_X_%s", textureName, psSamplerBinding->Name); + } + } + + return result; +} + +void ConcatTextureSamplerName(bstring str, + ShaderInfo* psShaderInfo, + const uint32_t ui32TextureRegisterNumber, + const uint32_t ui32SamplerRegisterNumber, + const int bZCompare) +{ + bstring texturesamplername = TextureSamplerName(psShaderInfo, ui32TextureRegisterNumber, ui32SamplerRegisterNumber, bZCompare); + bconcat(str, texturesamplername); + bdestroy(texturesamplername); +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toMETAL.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETAL.c new file mode 100644 index 0000000000..8e3a719950 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETAL.c @@ -0,0 +1,440 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/tokens.h" +#include "internal_includes/structs.h" +#include "internal_includes/decode.h" +#include "stdlib.h" +#include "stdio.h" +#include "bstrlib.h" +#include "internal_includes/toMETALInstruction.h" +#include "internal_includes/toMETALOperand.h" +#include "internal_includes/toMETALDeclaration.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/structsMetal.h" + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); +extern void UpdateFullName(ShaderVarType* psParentVarType); +extern void MangleIdentifiersPerStage(ShaderData* psShader); + + +void TranslateToMETAL(HLSLCrossCompilerContext* psContext, ShaderLang* planguage) +{ + bstring metal; + uint32_t i; + ShaderData* psShader = psContext->psShader; + ShaderLang language = *planguage; + uint32_t ui32InstCount = 0; + uint32_t ui32DeclCount = 0; + + psContext->indent = 0; + + /*psShader->sPhase[MAIN_PHASE].ui32InstanceCount = 1; + psShader->sPhase[MAIN_PHASE].ppsDecl = hlslcc_malloc(sizeof(Declaration*)); + psShader->sPhase[MAIN_PHASE].ppsInst = hlslcc_malloc(sizeof(Instruction*)); + psShader->sPhase[MAIN_PHASE].pui32DeclCount = hlslcc_malloc(sizeof(uint32_t)); + psShader->sPhase[MAIN_PHASE].pui32InstCount = hlslcc_malloc(sizeof(uint32_t));*/ + + if(language == LANG_DEFAULT) + { + language = LANG_METAL; + *planguage = language; + } + + metal = bfromcstralloc (1024, ""); + + psContext->mainShader = metal; + psContext->stagedInputDeclarations = bfromcstralloc(1024, ""); + psContext->parameterDeclarations = bfromcstralloc(1024, ""); + psContext->declaredOutputs = bfromcstralloc(1024, ""); + psContext->earlyMain = bfromcstralloc (1024, ""); + for(i=0; ipostShaderCode[i] = bfromcstralloc (1024, ""); + } + + psContext->needsFragmentTestHint = 0; + + for (i = 0; i < MAX_COLOR_MRT; i++) + psContext->gmemOutputNumElements[i] = 0; + + psContext->currentShaderString = &metal; + psShader->eTargetLanguage = language; + psContext->currentPhase = MAIN_PHASE; + + bcatcstr(metal, "#include \n"); + bcatcstr(metal, "using namespace metal;\n"); + + + bcatcstr(metal, "struct float1 {\n"); + bcatcstr(metal, "\tfloat x;\n"); + bcatcstr(metal, "};\n"); + + bcatcstr(metal, "struct uint1 {\n"); + bcatcstr(metal, "\tuint x;\n"); + bcatcstr(metal, "};\n"); + + bcatcstr(metal, "struct int1 {\n"); + bcatcstr(metal, "\tint x;\n"); + bcatcstr(metal, "};\n"); + + + ui32InstCount = psShader->asPhase[MAIN_PHASE].pui32InstCount[0]; + ui32DeclCount = psShader->asPhase[MAIN_PHASE].pui32DeclCount[0]; + + AtomicVarList atomicList; + atomicList.Filled = 0; + atomicList.Size = ui32InstCount; + atomicList.AtomicVars = (const ShaderVarType**)hlslcc_malloc(ui32InstCount * sizeof(ShaderVarType*)); + + for (i = 0; i < ui32InstCount; ++i) + { + DetectAtomicInstructionMETAL(psContext, psShader->asPhase[MAIN_PHASE].ppsInst[0] + i, i + 1 < ui32InstCount ? psShader->asPhase[MAIN_PHASE].ppsInst[0] + i + 1 : 0, &atomicList); + } + + for(i=0; i < ui32DeclCount; ++i) + { + TranslateDeclarationMETAL(psContext, psShader->asPhase[MAIN_PHASE].ppsDecl[0] + i, &atomicList); + } + + if(psContext->psShader->ui32NumDx9ImmConst) + { + bformata(psContext->mainShader, "float4 ImmConstArray [%d];\n", psContext->psShader->ui32NumDx9ImmConst); + } + + MarkIntegerImmediatesMETAL(psContext); + + SetDataTypesMETAL(psContext, psShader->asPhase[MAIN_PHASE].ppsInst[0], ui32InstCount); + + switch (psShader->eShaderType) + { + case VERTEX_SHADER: + { + int hasStageInput = 0; + int hasOutput = 0; + if (blength(psContext->stagedInputDeclarations) > 0) + { + hasStageInput = 1; + bcatcstr(metal, "struct metalVert_stageIn\n{\n"); + bconcat(metal, psContext->stagedInputDeclarations); + bcatcstr(metal, "};\n"); + } + if (blength(psContext->declaredOutputs) > 0) + { + hasOutput = 1; + bcatcstr(metal, "struct metalVert_out\n{\n"); + bconcat(metal, psContext->declaredOutputs); + bcatcstr(metal, "};\n"); + } + + bformata(metal, "vertex %s metalMain(\n%s", + hasOutput ? "metalVert_out" : "void", + hasStageInput ? "\tmetalVert_stageIn stageIn [[ stage_in ]]" : ""); + + int userInputDeclLength = blength(psContext->parameterDeclarations); + if (userInputDeclLength > 2) + { + if (hasStageInput) + bformata(metal, ",\n"); + bdelete(psContext->parameterDeclarations, userInputDeclLength - 2, 2); // remove ",\n" + } + + bconcat(metal, psContext->parameterDeclarations); + bcatcstr(metal, hasOutput ? "\t)\n{\n\tmetalVert_out output;\n" : ")\n{\n"); + break; + } + case PIXEL_SHADER: + { + int hasStageInput = 0; + int hasOutput = 0; + int userInputDeclLength = blength(psContext->parameterDeclarations); + if (blength(psContext->stagedInputDeclarations) > 0) + { + hasStageInput = 1; + bcatcstr(metal, "struct metalFrag_stageIn\n{\n"); + bconcat(metal, psContext->stagedInputDeclarations); + bcatcstr(metal, "};\n"); + } + if (blength(psContext->declaredOutputs) > 0) + { + hasOutput = 1; + bcatcstr(metal, "struct metalFrag_out\n{\n"); + bconcat(metal, psContext->declaredOutputs); + bcatcstr(metal, "};\n"); + } + + bcatcstr(metal, "fragment "); + if (psContext->needsFragmentTestHint) + { + bcatcstr(metal, "\n#ifndef MTLLanguage1_1\n"); + bcatcstr(metal, "[[ early_fragment_tests ]]\n"); + bcatcstr(metal, "#endif\n"); + } + + bformata(metal, "%s metalMain(\n%s", hasOutput ? "metalFrag_out" : "void", + hasStageInput ? "\tmetalFrag_stageIn stageIn [[ stage_in ]]" : ""); + if (userInputDeclLength > 2) + { + if (hasStageInput) + bcatcstr(metal, ",\n"); + bdelete(psContext->parameterDeclarations, userInputDeclLength - 2, 2); // remove the trailing comma and space + } + bconcat(metal, psContext->parameterDeclarations); + bcatcstr(metal, hasOutput ? ")\n{\n\tmetalFrag_out output;\n" : ")\n{\n"); + break; + } + case COMPUTE_SHADER: + { + int hasStageInput = 0; + int hasOutput = 0; + if (blength(psContext->stagedInputDeclarations) > 0) + { + hasStageInput = 1; + bcatcstr(metal, "struct metalCompute_stageIn\n{\n"); + bconcat(metal, psContext->stagedInputDeclarations); + bcatcstr(metal, "};\n"); + } + if (blength(psContext->declaredOutputs) > 0) + { + hasOutput = 1; + bcatcstr(metal, "struct metalCompute_out\n{\n"); + bconcat(metal, psContext->declaredOutputs); + bcatcstr(metal, "};\n"); + } + + bformata(metal, "kernel %s metalMain(\n%s", + hasOutput ? "metalCompute_out" : "void", + hasStageInput ? "\tmetalCompute_stageIn stageIn [[ stage_in ]]" : ""); + + int userInputDeclLength = blength(psContext->parameterDeclarations); + if (userInputDeclLength > 2) + { + if (hasStageInput) + bformata(metal, ",\n"); + bdelete(psContext->parameterDeclarations, userInputDeclLength - 2, 2); // remove ",\n" + } + + bconcat(metal, psContext->parameterDeclarations); + bcatcstr(metal, hasOutput ? "\t)\n{\n\tmetalCompute_out output;\n" : ")\n{\n"); + break; + } + default: + { + ASSERT(0); + // Geometry, Hull, and Domain shaders unsupported by Metal + // int userInputDeclLength = blength(psContext->parameterDeclarations); + // if (blength(psContext->outputDeclarations) > 0) + // { + // bcatcstr(metal, "struct metalComp_out\n{\n"); + // bconcat(metal, psContext->outputDeclarations); + // bcatcstr(metal, "};\n"); + // if (userInputDeclLength > 2) + // bdelete(psContext->parameterDeclarations, userInputDeclLength - 2, 2); // remove the trailing comma and space + // bformata(metal, "kernel metalComp_out metalMain(%s)\n{\n\tmetalComp_out output;\n", psContext->parameterDeclarations); + // } + // else + // { + // if (userInputDeclLength > 2) + // bdelete(psContext->parameterDeclarations, userInputDeclLength - 2, 2); // remove the trailing comma and space + // bformata(metal, "kernel void metalMain(%s)\n{\n", psContext->parameterDeclarations); + // } + break; + } + } + + psContext->indent++; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//--- Start Early Main ---\n"); +#endif + bconcat(metal, psContext->earlyMain); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//--- End Early Main ---\n"); +#endif + + + for(i=0; i < ui32InstCount; ++i) + { + TranslateInstructionMETAL(psContext, psShader->asPhase[MAIN_PHASE].ppsInst[0]+i, i+1 < ui32InstCount ? psShader->asPhase[MAIN_PHASE].ppsInst[0]+i+1 : 0); + } + + hlslcc_free((void*)atomicList.AtomicVars); + + psContext->indent--; + + bcatcstr(metal, "}\n"); +} + +static void FreeSubOperands(Instruction* psInst, const uint32_t ui32NumInsts) +{ + uint32_t ui32Inst; + for(ui32Inst = 0; ui32Inst < ui32NumInsts; ++ui32Inst) + { + Instruction* psCurrentInst = &psInst[ui32Inst]; + const uint32_t ui32NumOperands = psCurrentInst->ui32NumOperands; + uint32_t ui32Operand; + + for(ui32Operand = 0; ui32Operand < ui32NumOperands; ++ui32Operand) + { + uint32_t ui32SubOperand; + for(ui32SubOperand = 0; ui32SubOperand < MAX_SUB_OPERANDS; ++ui32SubOperand) + { + if(psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand]) + { + hlslcc_free(psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand]); + psCurrentInst->asOperands[ui32Operand].psSubOperand[ui32SubOperand] = NULL; + } + } + } + } +} + +typedef enum { + MTLFunctionTypeVertex = 1, + MTLFunctionTypeFragment = 2, + MTLFunctionTypeKernel = 3 +} MTLFunctionType; + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromMemToMETAL(const char* shader, + unsigned int flags, + ShaderLang language, + Shader* result) +{ + uint32_t* tokens; + ShaderData* psShader; + char* glslcstr = NULL; + int ShaderType = MTLFunctionTypeFragment; + int success = 0; + uint32_t i; + + tokens = (uint32_t*)shader; + + psShader = DecodeDXBC(tokens); + + if(psShader) + { + HLSLCrossCompilerContext sContext; + + sContext.psShader = psShader; + sContext.flags = flags; + + for(i=0; ieShaderType) + { + case VERTEX_SHADER: + { + ShaderType = MTLFunctionTypeVertex; + break; + } + case COMPUTE_SHADER: + { + ShaderType = MTLFunctionTypeKernel; + break; + } + default: + { + break; + } + } + + glslcstr = bstr2cstr(sContext.mainShader, '\0'); + + bdestroy(sContext.mainShader); + bdestroy(sContext.earlyMain); + for(i=0; iasPhase[i].ppsDecl != 0) + { + uint32_t k; + for(k=0; k < psShader->asPhase[i].ui32InstanceCount; ++k) + { + hlslcc_free(psShader->asPhase[i].ppsDecl[k]); + } + hlslcc_free(psShader->asPhase[i].ppsDecl); + } + if(psShader->asPhase[i].ppsInst != 0) + { + uint32_t k; + for(k=0; k < psShader->asPhase[i].ui32InstanceCount; ++k) + { + FreeSubOperands(psShader->asPhase[i].ppsInst[k], psShader->asPhase[i].pui32InstCount[k]); + hlslcc_free(psShader->asPhase[i].ppsInst[k]); + } + hlslcc_free(psShader->asPhase[i].ppsInst); + } + } + + memcpy(&result->reflection,&psShader->sInfo,sizeof(psShader->sInfo)); + + result->textureSamplerInfo.ui32NumTextureSamplerPairs = psShader->textureSamplerInfo.ui32NumTextureSamplerPairs; + for (i=0; itextureSamplerInfo.ui32NumTextureSamplerPairs; i++) + strcpy(result->textureSamplerInfo.aTextureSamplerPair[i].Name, psShader->textureSamplerInfo.aTextureSamplerPair[i].Name); + + hlslcc_free(psShader); + + success = 1; + } + + shader = 0; + tokens = 0; + + /* Fill in the result struct */ + + result->shaderType = ShaderType; + result->sourceCode = glslcstr; + result->GLSLLanguage = language; + + return success; +} + +HLSLCC_API int HLSLCC_APIENTRY TranslateHLSLFromFileToMETAL(const char* filename, + unsigned int flags, + ShaderLang language, + Shader* result) +{ + FILE* shaderFile; + int length; + size_t readLength; + char* shader; + int success = 0; + + shaderFile = fopen(filename, "rb"); + + if(!shaderFile) + { + return 0; + } + + fseek(shaderFile, 0, SEEK_END); + length = ftell(shaderFile); + fseek(shaderFile, 0, SEEK_SET); + + shader = (char*)hlslcc_malloc(length+1); + + readLength = fread(shader, 1, length, shaderFile); + + fclose(shaderFile); + shaderFile = 0; + + shader[readLength] = '\0'; + + success = TranslateHLSLFromMemToMETAL(shader, flags, language, result); + + hlslcc_free(shader); + + return success; +} \ No newline at end of file diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALDeclaration.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALDeclaration.c new file mode 100644 index 0000000000..79dcb809fd --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALDeclaration.c @@ -0,0 +1,2281 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "hlslcc.h" +#include "internal_includes/toMETALDeclaration.h" +#include "internal_includes/toMETALOperand.h" +#include "internal_includes/languages.h" +#include "bstrlib.h" +#include "internal_includes/debug.h" +#include "internal_includes/hlslcc_malloc.h" +#include "internal_includes/structsMetal.h" +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wpointer-sign" +#endif + +#ifdef _MSC_VER +#ifndef isnan +#define isnan(x) _isnan(x) +#endif + +#ifndef isinf +#define isinf(x) (!_finite(x)) +#endif +#endif + +#define fpcheck(x) (isnan(x) || isinf(x)) + +typedef enum +{ + GLVARTYPE_FLOAT, + GLVARTYPE_INT, + GLVARTYPE_FLOAT4, +} GLVARTYPE; + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); + +const char* GetTypeStringMETAL(GLVARTYPE eType) +{ + switch (eType) + { + case GLVARTYPE_FLOAT: + { + return "float"; + } + case GLVARTYPE_INT: + { + return "int"; + } + case GLVARTYPE_FLOAT4: + { + return "float4"; + } + default: + { + return ""; + } + } +} +const uint32_t GetTypeElementCountMETAL(GLVARTYPE eType) +{ + switch (eType) + { + case GLVARTYPE_FLOAT: + case GLVARTYPE_INT: + { + return 1; + } + case GLVARTYPE_FLOAT4: + { + return 4; + } + default: + { + return 0; + } + } +} + +void AddToDx9ImmConstIndexableArrayMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + bstring* savedStringPtr = psContext->currentShaderString; + + psContext->currentShaderString = &psContext->earlyMain; + psContext->indent++; + AddIndentation(psContext); + psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber] = psContext->psShader->ui32NumDx9ImmConst; + bformata(psContext->earlyMain, "ImmConstArray[%d] = ", psContext->psShader->ui32NumDx9ImmConst); + TranslateOperandMETAL(psContext, psOperand, TO_FLAG_NONE); + bcatcstr(psContext->earlyMain, ";\n"); + psContext->indent--; + psContext->psShader->ui32NumDx9ImmConst++; + + psContext->currentShaderString = savedStringPtr; +} + +void DeclareConstBufferShaderVariableMETAL(bstring metal, const char* Name, const struct ShaderVarType_TAG* psType, int pointerType, int const createDummyAlignment, AtomicVarList* psAtomicList) +//const SHADER_VARIABLE_CLASS eClass, const SHADER_VARIABLE_TYPE eType, +//const char* pszName) +{ + if (psType->Class == SVC_STRUCT) + { + bformata(metal, "%s_Type %s%s", Name, pointerType ? "*" : "", Name); + if (psType->Elements > 1) + { + bformata(metal, "[%d]", psType->Elements); + } + } + else if (psType->Class == SVC_MATRIX_COLUMNS || psType->Class == SVC_MATRIX_ROWS) + { + switch (psType->Type) + { + case SVT_FLOAT: + { + bformata(metal, "\tfloat%d %s%s[%d", psType->Columns, pointerType ? "*" : "", Name, psType->Rows); + break; + } + case SVT_FLOAT16: + { + bformata(metal, "\thalf%d %s%s[%d", psType->Columns, pointerType ? "*" : "", Name, psType->Rows); + break; + } + default: + { + ASSERT(0); + break; + } + } + if (psType->Elements > 1) + { + bformata(metal, " * %d", psType->Elements); + } + bformata(metal, "]"); + } + else + if (psType->Class == SVC_VECTOR) + { + switch (psType->Type) + { + case SVT_DOUBLE: + case SVT_FLOAT: + { + bformata(metal, "\tfloat%d %s%s", psType->Columns, pointerType ? "*" : "", Name); + break; + } + case SVT_FLOAT16: + { + bformata(metal, "\thalf%d %s%s", psType->Columns, pointerType ? "*" : "", Name); + break; + } + case SVT_UINT: + { + bformata(metal, "\tuint%d %s%s", psType->Columns, pointerType ? "*" : "", Name); + break; + } + case SVT_INT: + case SVT_BOOL: + { + bformata(metal, "\tint%d %s%s", psType->Columns, pointerType ? "*" : "", Name); + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (psType->Elements > 1) + { + bformata(metal, "[%d]", psType->Elements); + } + } + else + if (psType->Class == SVC_SCALAR) + { + switch (psType->Type) + { + case SVT_DOUBLE: + case SVT_FLOAT: + { + bformata(metal, "\tfloat %s%s", pointerType ? "*" : "", Name); + break; + } + case SVT_FLOAT16: + { + bformata(metal, "\thalf %s%s", pointerType ? "*" : "", Name); + break; + } + case SVT_UINT: + { + if (IsAtomicVar(psType, psAtomicList)) + { + bformata(metal, "\tvolatile atomic_uint %s%s", pointerType ? "*" : "", Name); + } + else + { + bformata(metal, "\tuint %s%s", pointerType ? "*" : "", Name); + } + break; + } + case SVT_INT: + { + if (IsAtomicVar(psType, psAtomicList)) + { + bformata(metal, "\tvolatile atomic_int %s%s", pointerType ? "*" : "", Name); + } + else + { + bformata(metal, "\tint %s%s", pointerType ? "*" : "", Name); + } + break; + } + case SVT_BOOL: + { + //Use int instead of bool. + //Allows implicit conversions to integer and + //bool consumes 4-bytes in HLSL and metal anyway. + bformata(metal, "\tint %s%s", pointerType ? "*" : "", Name); + // Also change the definition in the type tree. + ((ShaderVarType*)psType)->Type = SVT_INT; + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (psType->Elements > 1) + { + bformata(metal, "[%d]", psType->Elements); + } + } + if (!pointerType) + { + bformata(metal, ";\n"); + } + + // We need to add more dummies if float2 or less since they are not 16 bytes aligned + // float = 4 + // float2 = 8 + // float3 = float4 = 16 + // https://developer.apple.com/library/ios/documentation/Metal/Reference/MetalShadingLanguageGuide/data-types/data-types.html + if (createDummyAlignment) + { + uint16_t sizeInBytes = 16; + if (1 == psType->Columns) + { + sizeInBytes = 4; + } + else if (2 == psType->Columns) + { + sizeInBytes = 8; + } + + if (4 == sizeInBytes) + { + bformata(metal, "\tfloat offsetDummy_4Bytes_%s;\n", Name); + bformata(metal, "\tfloat2 offsetDummy_8Bytes_%s;\n", Name); + } + else if (8 == sizeInBytes) + { + bformata(metal, "\tfloat2 offsetDummy_8Bytes_%s;\n", Name); + } + } +} + +//In metal embedded structure definitions are not supported. +void PreDeclareStructTypeMETAL(bstring metal, const char* Name, const struct ShaderVarType_TAG* psType, AtomicVarList* psAtomicList) +{ + uint32_t i; + + for (i = 0; i < psType->MemberCount; ++i) + { + if (psType->Members[i].Class == SVC_STRUCT) + { + PreDeclareStructTypeMETAL(metal, psType->Members[i].Name, &psType->Members[i], psAtomicList); + } + } + + if (psType->Class == SVC_STRUCT) + { +#if defined(_DEBUG) + uint32_t unnamed_struct = strcmp(Name, "$Element") == 0 ? 1 : 0; +#endif + + //Not supported at the moment + ASSERT(!unnamed_struct); + + bformata(metal, "struct %s_Type {\n", Name); + + for (i = 0; i < psType->MemberCount; ++i) + { + ASSERT(psType->Members != 0); + + DeclareConstBufferShaderVariableMETAL(metal, psType->Members[i].Name, &psType->Members[i], 0, 0, psAtomicList); + } + + bformata(metal, "};\n"); + } +} + +char* GetDeclaredInputNameMETAL(const HLSLCrossCompilerContext* psContext, const SHADER_TYPE eShaderType, const Operand* psOperand) +{ + bstring inputName; + char* cstr; + InOutSignature* psIn; + + if (eShaderType == PIXEL_SHADER) + { + inputName = bformat("VtxOutput%d", psOperand->ui32RegisterNumber); + } + else + { + ASSERT(eShaderType == VERTEX_SHADER); + inputName = bformat("dcl_Input%d", psOperand->ui32RegisterNumber); + } + if ((psContext->flags & HLSLCC_FLAG_INOUT_SEMANTIC_NAMES) && GetInputSignatureFromRegister(psOperand->ui32RegisterNumber, &psContext->psShader->sInfo, &psIn)) + { + bformata(inputName, "_%s%d", psIn->SemanticName, psIn->ui32SemanticIndex); + } + + cstr = bstr2cstr(inputName, '\0'); + bdestroy(inputName); + return cstr; +} + +char* GetDeclaredOutputNameMETAL(const HLSLCrossCompilerContext* psContext, + const SHADER_TYPE eShaderType, + const Operand* psOperand) +{ + bstring outputName = bformat(""); + char* cstr; + InOutSignature* psOut; + +#if defined(_DEBUG) + int foundOutput = +#endif + GetOutputSignatureFromRegister( + psContext->currentPhase, + psOperand->ui32RegisterNumber, + psOperand->ui32CompMask, + psContext->psShader->ui32CurrentVertexOutputStream, + &psContext->psShader->sInfo, + &psOut); + + ASSERT(foundOutput); + + if (eShaderType == VERTEX_SHADER) + { + outputName = bformat("VtxOutput%d", psOperand->ui32RegisterNumber); + } + else if (eShaderType == PIXEL_SHADER) + { + outputName = bformat("PixOutput%d", psOperand->ui32RegisterNumber); + } + + if (psContext->flags & HLSLCC_FLAG_INOUT_APPEND_SEMANTIC_NAMES) + { + bformata(outputName, "_%s%d", psOut->SemanticName, psOut->ui32SemanticIndex); + } + + cstr = bstr2cstr(outputName, '\0'); + bdestroy(outputName); + return cstr; +} + +const char* GetInterpolationStringMETAL(INTERPOLATION_MODE eMode) +{ + switch (eMode) + { + case INTERPOLATION_CONSTANT: + { + return "flat"; + } + case INTERPOLATION_LINEAR: + { + return "center_perspective"; + } + case INTERPOLATION_LINEAR_CENTROID: + { + return "centroid_perspective"; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE: + { + return "center_no_perspective"; + break; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_CENTROID: + { + return "centroid_no_perspective"; + } + case INTERPOLATION_LINEAR_SAMPLE: + { + return "sample_perspective"; + } + case INTERPOLATION_LINEAR_NOPERSPECTIVE_SAMPLE: + { + return "sample_no_perspective"; + } + default: + { + return ""; + } + } +} + +static void DeclareInput( + HLSLCrossCompilerContext* psContext, + const Declaration* psDecl, const char* StorageQualifier, OPERAND_MIN_PRECISION minPrecision, int iNumComponents, OPERAND_INDEX_DIMENSION eIndexDim, const char* InputName) +{ + ShaderData* psShader = psContext->psShader; + psContext->currentShaderString = &psContext->parameterDeclarations; + bstring metal = *psContext->currentShaderString; + + // This falls within the specified index ranges. The default is 0 if no input range is specified + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + { + return; + } + + if (psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == 0) + { + + InOutSignature* psSignature = NULL; + + const char* type = "float"; + if (minPrecision == OPERAND_MIN_PRECISION_FLOAT_16) + { + type = "half"; + } + if (GetInputSignatureFromRegister(psDecl->asOperands[0].ui32RegisterNumber, &psShader->sInfo, &psSignature)) + { + switch (psSignature->eComponentType) + { + case INOUT_COMPONENT_UINT32: + { + type = "uint"; + break; + } + case INOUT_COMPONENT_SINT32: + { + type = "int"; + break; + } + case INOUT_COMPONENT_FLOAT32: + { + break; + } + } + } + + bstring qual = bfromcstralloc(256, StorageQualifier); + + if (biseqcstr(qual, "attribute")) + { + bformata(qual, "(%d)", psDecl->asOperands[0].ui32RegisterNumber); + psContext->currentShaderString = &psContext->stagedInputDeclarations; + metal = *psContext->currentShaderString; + } + else if (biseqcstr(qual, "user")) + { + bformata(qual, "(varying%d)", psDecl->asOperands[0].ui32RegisterNumber); + psContext->currentShaderString = &psContext->stagedInputDeclarations; + metal = *psContext->currentShaderString; + } + else if (biseqcstr(qual, "buffer")) + { + bformata(qual, "(%d)", psDecl->asOperands[0].ui32RegisterNumber); + } + + if (metal == psContext->stagedInputDeclarations) + { + bformata(metal, "\t%s", type); + if (iNumComponents > 1) + { + bformata(metal, "%d", iNumComponents); + } + } + else + { + if (iNumComponents > 1) + { + bformata(metal, "\tdevice %s%d*", type, iNumComponents); + } + else + { + bformata(metal, "\tdevice %s*", type, iNumComponents); + } + } + + + if (psDecl->asOperands[0].eType == OPERAND_TYPE_SPECIAL_TEXCOORD) + { + InputName = "TexCoord"; + } + + bformata(metal, " %s", InputName); + + switch (eIndexDim) + { + case INDEX_2D: + { + if (iNumComponents == 1) + { + psContext->psShader->abScalarInput[psDecl->asOperands[0].ui32RegisterNumber] = -1; + } + + const uint32_t arraySize = psDecl->asOperands[0].aui32ArraySizes[0]; + + bformata(metal, " [%d]", arraySize); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = arraySize; + break; + } + default: + { + if (iNumComponents == 1) + { + psContext->psShader->abScalarInput[psDecl->asOperands[0].ui32RegisterNumber] = 1; + } + else + { + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] > 0) + { + bformata(metal, "[%d]", type, iNumComponents, InputName, + psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]); + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]; + } + else + { + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = -1; + } + } + break; + } + } + + if (blength(qual) > 0) + { + bformata(metal, " [[ %s ]]", bdata(qual)); + } + bdestroy(qual); + + bformata(metal, "%c\n", (metal == psContext->stagedInputDeclarations) ? ';' : ','); + + if (psShader->abInputReferencedByInstruction[psDecl->asOperands[0].ui32RegisterNumber]) + { + const char* stageInString = (metal == psContext->stagedInputDeclarations) ? "stageIn." : ""; + const char* bufferAccessString = (metal == psContext->stagedInputDeclarations) ? "" : "[vId]"; + + psContext->currentShaderString = &psContext->earlyMain; + metal = *psContext->currentShaderString; + psContext->indent++; + + if (psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == -1) //Not an array + { + AddIndentation(psContext); + bformata(metal, "%s%d Input%d = %s%s%s;\n", type, iNumComponents, + psDecl->asOperands[0].ui32RegisterNumber, stageInString, InputName, bufferAccessString); + } + else + { + int arrayIndex = psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber]; + bformata(metal, "%s%d Input%d[%d];\n", type, iNumComponents, psDecl->asOperands[0].ui32RegisterNumber, + psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber]); + + while (arrayIndex) + { + AddIndentation(psContext); + bformata(metal, "Input%d[%d] = %s%s%s[%d];\n", psDecl->asOperands[0].ui32RegisterNumber, arrayIndex - 1, + stageInString, InputName, bufferAccessString, arrayIndex - 1); + + arrayIndex--; + } + } + psContext->indent--; + } + } + psContext->currentShaderString = &psContext->mainShader; +} + +static void AddBuiltinInputMETAL(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, const char* builtinName, const char* type) +{ + psContext->currentShaderString = &psContext->stagedInputDeclarations; + bstring metal = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + char* InputName = GetDeclaredInputNameMETAL(psContext, PIXEL_SHADER, &psDecl->asOperands[0]); + + if (psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] == 0) + { + // CONFETTI NOTE: DAVID SROUR + // vertex_id and instance_id must be part of the function's params -- not part of stage_in! + if (psDecl->asOperands[0].eSpecialName == NAME_INSTANCE_ID || psDecl->asOperands[0].eSpecialName == NAME_VERTEX_ID) + { + bformata(psContext->parameterDeclarations, "\t%s %s [[ %s ]],\n", type, &psDecl->asOperands[0].pszSpecialName, builtinName); + } + else + { + bformata(metal, "\t%s %s [[ %s ]];\n", type, InputName, builtinName); + } + + psShader->aiInputDeclaredSize[psDecl->asOperands[0].ui32RegisterNumber] = 1; + } + + if (psShader->abInputReferencedByInstruction[psDecl->asOperands[0].ui32RegisterNumber]) + { + psContext->currentShaderString = &psContext->earlyMain; + metal = *psContext->currentShaderString; + psContext->indent++; + AddIndentation(psContext); + + if (psDecl->asOperands[0].eSpecialName == NAME_INSTANCE_ID || psDecl->asOperands[0].eSpecialName == NAME_VERTEX_ID) + { + bformata(metal, "uint4 "); + bformata(metal, "Input%d; Input%d.x = %s;\n", + psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber, &psDecl->asOperands[0].pszSpecialName); + } + else if (!strcmp(type, "bool")) + { + bformata(metal, "int4 "); + bformata(metal, "Input%d; Input%d.x = stageIn.%s;\n", + psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + else if (!strcmp(type, "float")) + { + bformata(metal, "float4 "); + bformata(metal, "Input%d; Input%d.x = stageIn.%s;\n", + psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + else if (!strcmp(type, "int")) + { + bformata(metal, "int4 "); + bformata(metal, "Input%d; Input%d.x = stageIn.%s;\n", + psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + else if (!strcmp(type, "uint")) + { + bformata(metal, "uint4 "); + bformata(metal, "Input%d; Input%d.x = stageIn.%s;\n", + psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + else + { + bformata(metal, "%s Input%d = stageIn.%s;\n", type, + psDecl->asOperands[0].ui32RegisterNumber, InputName); + } + + if (psDecl->asOperands[0].eSpecialName == NAME_POSITION) + { + if (psContext->psShader->eShaderType == PIXEL_SHADER) + { + if (psDecl->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE && + psDecl->asOperands[0].eType == OPERAND_TYPE_INPUT) + { + if (psDecl->asOperands[0].ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + bformata(metal, "Input%d.w = 1.0 / Input%d.w;", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber); + } + } + } + } + + psContext->indent--; + } + bcstrfree(InputName); + + psContext->currentShaderString = &psContext->mainShader; +} + +int OutputNeedsDeclaringMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, const int count) +{ + ShaderData* psShader = psContext->psShader; + + // Depth Output operands are a special case and won't have a ui32RegisterNumber, + // so first we have to check if the output operand is depth. + if (psShader->eShaderType == PIXEL_SHADER) + { + if (psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL || + psOperand->eType == OPERAND_TYPE_OUTPUT_DEPTH) + { + return 1; + } + } + + const uint32_t declared = ((psContext->currentPhase + 1) << 3) | psShader->ui32CurrentVertexOutputStream; + ASSERT(psOperand->ui32RegisterNumber >= 0); + ASSERT(psOperand->ui32RegisterNumber < MAX_SHADER_VEC4_OUTPUT); + if (psShader->aiOutputDeclared[psOperand->ui32RegisterNumber] != declared) + { + int offset; + + for (offset = 0; offset < count; offset++) + { + psShader->aiOutputDeclared[psOperand->ui32RegisterNumber + offset] = declared; + } + return 1; + } + + return 0; +} + +void AddBuiltinOutputMETAL(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, const GLVARTYPE type, int arrayElements, const char* builtinName) +{ + (void)type; + + bstring metal = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + psContext->havePostShaderCode[psContext->currentPhase] = 1; + + if (OutputNeedsDeclaringMETAL(psContext, &psDecl->asOperands[0], arrayElements ? arrayElements : 1)) + { + psContext->currentShaderString = &psContext->declaredOutputs; + metal = *psContext->currentShaderString; + InOutSignature* psSignature = NULL; + + int regNum = psDecl->asOperands[0].ui32RegisterNumber; + + GetOutputSignatureFromRegister(psContext->currentPhase, regNum, + psDecl->asOperands[0].ui32CompMask, + 0, + &psShader->sInfo, &psSignature); + + if (psDecl->asOperands[0].eSpecialName == NAME_CLIP_DISTANCE) + { + int max = GetMaxComponentFromComponentMaskMETAL(&psDecl->asOperands[0]); + bformata(metal, "\tfloat %s [%d] [[ %s ]];\n", builtinName, max, builtinName); + } + else + { + bformata(metal, "\tfloat4 %s [[ %s ]];\n", builtinName, builtinName); + } + bformata(metal, "#define Output%d output.%s\n", regNum, builtinName); + + psContext->currentShaderString = &psContext->mainShader; + } +} + +void AddUserOutputMETAL(HLSLCrossCompilerContext* psContext, const Declaration* psDecl) +{ + psContext->currentShaderString = &psContext->declaredOutputs; + bstring metal = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + if (OutputNeedsDeclaringMETAL(psContext, &psDecl->asOperands[0], 1)) + { + const Operand* psOperand = &psDecl->asOperands[0]; + const char* type = "\tfloat"; + const SHADER_VARIABLE_TYPE eOutType = GetOperandDataTypeMETAL(psContext, &psDecl->asOperands[0]); + + switch (eOutType) + { + case SVT_UINT: + { + type = "\tuint"; + break; + } + case SVT_INT: + { + type = "\tint"; + break; + } + case SVT_FLOAT16: + { + type = "\thalf"; + break; + } + case SVT_FLOAT: + { + break; + } + } + + switch (psShader->eShaderType) + { + case PIXEL_SHADER: + { + switch (psDecl->asOperands[0].eType) + { + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + { + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH: + { + bformata(metal, "%s PixOutDepthAny [[ depth(any) ]];\n", type); + bformata(metal, "#define DepthAny output.PixOutDepthAny\n"); + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + { + bformata(metal, "%s PixOutDepthGreater [[ depth(greater) ]];\n", type); + bformata(metal, "#define DepthGreater output.PixOutDepthGreater\n"); + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + { + bformata(metal, "%s PixOutDepthLess [[ depth(less) ]];\n", type); + bformata(metal, "#define DepthLess output.PixOutDepthLess\n"); + break; + } + default: + { + uint32_t renderTarget = psDecl->asOperands[0].ui32RegisterNumber; + + if (!psContext->gmemOutputNumElements[psDecl->asOperands[0].ui32RegisterNumber]) + { + bformata(metal, "%s4 PixOutColor%d [[ color(%d) ]];\n", type, renderTarget, renderTarget); + } + else // GMEM output type must match the input! + { + bformata(metal, "float%d PixOutColor%d [[ color(%d) ]];\n", psContext->gmemOutputNumElements[psDecl->asOperands[0].ui32RegisterNumber], renderTarget, renderTarget); + } + bformata(metal, "#define Output%d output.PixOutColor%d\n", psDecl->asOperands[0].ui32RegisterNumber, renderTarget); + + break; + } + } + break; + } + case VERTEX_SHADER: + { + int iNumComponents = 4;//GetMaxComponentFromComponentMaskMETAL(&psDecl->asOperands[0]); + char* OutputName = GetDeclaredOutputNameMETAL(psContext, VERTEX_SHADER, psOperand); + + bformata(metal, "%s%d %s [[ user(varying%d) ]];\n", type, iNumComponents, OutputName, psDecl->asOperands[0].ui32RegisterNumber); + bformata(metal, "#define Output%d output.%s\n", psDecl->asOperands[0].ui32RegisterNumber, OutputName); + bcstrfree(OutputName); + + break; + } + } + } + + psContext->currentShaderString = &psContext->mainShader; +} + +void DeclareBufferVariableMETAL(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, + ConstantBuffer* psCBuf, const Operand* psOperand, + const ResourceType eResourceType, + bstring metal, AtomicVarList* psAtomicList) +{ + (void)ui32BindingPoint; + + bstring StructName; +#if !defined(NDEBUG) + uint32_t unnamed_struct = strcmp(psCBuf->asVars[0].Name, "$Element") == 0 ? 1 : 0; +#endif + + ASSERT(psCBuf->ui32NumVars == 1); + ASSERT(unnamed_struct); + + StructName = bfromcstr(""); + + //TranslateOperandMETAL(psContext, psOperand, TO_FLAG_NAME_ONLY); + if (psOperand->eType == OPERAND_TYPE_RESOURCE && eResourceType == RTYPE_STRUCTURED) + { + ResourceNameMETAL(StructName, psContext, RGROUP_TEXTURE, psOperand->ui32RegisterNumber, 0); + } + else if (psOperand->eType == OPERAND_TYPE_RESOURCE && eResourceType == RTYPE_UAV_RWBYTEADDRESS) + { + bformata(StructName, "RawRes%d", psOperand->ui32RegisterNumber); + } + else + { + ResourceNameMETAL(StructName, psContext, RGROUP_UAV, psOperand->ui32RegisterNumber, 0); + } + + PreDeclareStructTypeMETAL(metal, + bstr2cstr(StructName, '\0'), + &psCBuf->asVars[0].sType, psAtomicList); + + + bcatcstr(psContext->parameterDeclarations, "\t"); + if (eResourceType == RTYPE_STRUCTURED) + { + bcatcstr(psContext->parameterDeclarations, "constant "); + } + else + { + bcatcstr(psContext->parameterDeclarations, "device "); + } + + + DeclareConstBufferShaderVariableMETAL(psContext->parameterDeclarations, + bstr2cstr(StructName, '\0'), + &psCBuf->asVars[0].sType, + 1, 0, psAtomicList); + if (eResourceType == RTYPE_UAV_RWSTRUCTURED) + { + //If it is UAV raw structured, let Metal compiler assign it with the first available location index + bformata(psContext->parameterDeclarations, " [[ buffer(%d) ]],\n", psOperand->ui32RegisterNumber + UAV_BUFFER_START_SLOT); + //modify the reflection data to match the binding index + int count = 0; + for (uint32_t index = 0; index < psContext->psShader->sInfo.ui32NumResourceBindings; index++) + { + if (strcmp(psContext->psShader->sInfo.psResourceBindings[index].Name, (const char*)StructName->data) == 0) + { + count++; + //psContext->psShader->sInfo.psResourceBindings[index].ui32BindPoint += UAV_BUFFER_START_SLOT; + psContext->psShader->sInfo.psResourceBindings[index].eBindArea = UAVAREA_CBUFFER; + } + } + //If count >2, the logic here is wrong and need to be modified. + ASSERT(count < 2); + } + else + { + bformata(psContext->parameterDeclarations, " [[ buffer(%d) ]],\n", psOperand->ui32RegisterNumber); + } + + bdestroy(StructName); +} + +static uint32_t ComputeVariableTypeSize(const ShaderVarType* psType) +{ + if (psType->Class == SVC_STRUCT) + { + uint32_t i; + uint32_t size = 0; + for (i = 0; i < psType->MemberCount; ++i) + { + size += ComputeVariableTypeSize(&psType->Members[i]); + } + + if (psType->Elements > 1) + { + return size * psType->Elements; + } + else + { + return size; + } + } + else if (psType->Class == SVC_MATRIX_COLUMNS || psType->Class == SVC_MATRIX_ROWS) + { + if (psType->Elements > 1) + { + return psType->Rows * psType->Elements; + } + else + { + return psType->Rows; + } + } + else + if (psType->Class == SVC_VECTOR) + { + if (psType->Elements > 1) + { + return psType->Elements; + } + else + { + return 1; + } + } + + return 1; +} + + +void DeclareStructConstantsMETAL(HLSLCrossCompilerContext* psContext, const uint32_t ui32BindingPoint, + ConstantBuffer* psCBuf, const Operand* psOperand, + bstring metal, AtomicVarList* psAtomicList) +{ + (void)psOperand; + + uint32_t i; + const char* StageName = "VS"; + uint32_t nextBufferRegister = 0; + uint32_t numDummyBuffers = 0; + + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + PreDeclareStructTypeMETAL(metal, + psCBuf->asVars[i].sType.Name, + &psCBuf->asVars[i].sType, psAtomicList); + } + + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + StageName = "PS"; + break; + } + case COMPUTE_SHADER: + { + StageName = "CS"; + break; + } + default: + { + break; + } + } + + bformata(metal, "struct %s%s_Type {\n", psCBuf->Name, StageName); + + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + uint32_t ui32RegNum = psCBuf->asVars[i].ui32StartOffset / 16; + if (ui32RegNum > nextBufferRegister) + { + bformata(metal, "\tfloat4 offsetDummy_%d[%d];\n", numDummyBuffers++, ui32RegNum - nextBufferRegister); + } + + DeclareConstBufferShaderVariableMETAL(metal, + psCBuf->asVars[i].sType.Name, + &psCBuf->asVars[i].sType, 0, i < psCBuf->ui32NumVars - 1, psAtomicList); + + uint32_t varSize = ComputeVariableTypeSize(&psCBuf->asVars[i].sType); + nextBufferRegister = ui32RegNum + varSize; + } + + bcatcstr(metal, "};\n"); + + bcatcstr(psContext->parameterDeclarations, "\tconstant "); + bformata(psContext->parameterDeclarations, "%s%s_Type ", psCBuf->Name, StageName); + bcatcstr(psContext->parameterDeclarations, "& "); + + bformata(psContext->parameterDeclarations, "%s%s_In", psCBuf->Name, StageName); + bformata(psContext->parameterDeclarations, " [[ buffer(%d) ]],\n", ui32BindingPoint); + + for (i = 0; i < psCBuf->ui32NumVars; ++i) + { + const struct ShaderVarType_TAG* psType = &psCBuf->asVars[i].sType; + const char* Name = psCBuf->asVars[i].sType.Name; + const char* addressSpace = "constant"; + + if (psType->Class == SVC_STRUCT) + { + bformata(psContext->earlyMain, "\t%s %s_Type%s const &%s", addressSpace, Name, psType->Elements > 1 ? "*" : "", Name); + } + else if (psType->Class == SVC_MATRIX_COLUMNS || psType->Class == SVC_MATRIX_ROWS) + { + switch (psType->Type) + { + case SVT_FLOAT: + { + bformata(psContext->earlyMain, "\t%s float%d%s const &%s", addressSpace, psType->Columns, "*", Name, psType->Rows); + break; + } + case SVT_FLOAT16: + { + bformata(psContext->earlyMain, "\t%s half%d%s const &%s", addressSpace, psType->Columns, "*", Name, psType->Rows); + break; + } + default: + { + ASSERT(0); + break; + } + } + } + else + if (psType->Class == SVC_VECTOR) + { + switch (psType->Type) + { + case SVT_FLOAT: + case SVT_DOUBLE: // double is not supported in metal + { + bformata(psContext->earlyMain, "\t%s float%d%s const &%s", addressSpace, psType->Columns, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_FLOAT16: + { + bformata(psContext->earlyMain, "\t%s half%d%s const &%s", addressSpace, psType->Columns, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_UINT: + { + bformata(psContext->earlyMain, "\t%s uint%d%s const &%s", addressSpace, psType->Columns, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_INT: + { + bformata(psContext->earlyMain, "\t%s int%d%s const &%s", addressSpace, psType->Columns, psType->Elements > 1 ? "*" : "", Name); + break; + } + default: + { + ASSERT(0); + break; + } + } + } + else + if (psType->Class == SVC_SCALAR) + { + switch (psType->Type) + { + case SVT_FLOAT: + case SVT_DOUBLE: // double is not supported in metal + { + bformata(psContext->earlyMain, "\t%s float%s const &%s", addressSpace, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_FLOAT16: + { + bformata(psContext->earlyMain, "\t%s half%s const &%s", addressSpace, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_UINT: + { + bformata(psContext->earlyMain, "\t%s uint%s const &%s", addressSpace, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_INT: + { + bformata(psContext->earlyMain, "\t%s int%s const &%s", addressSpace, psType->Elements > 1 ? "*" : "", Name); + break; + } + case SVT_BOOL: + { + //Use int instead of bool. + //Allows implicit conversions to integer + bformata(psContext->earlyMain, "\t%s int%s const &%s", addressSpace, psType->Elements > 1 ? "*" : "", Name); + break; + } + default: + { + ASSERT(0); + break; + } + } + } + + bformata(psContext->earlyMain, " = %s%s_In.%s;\n", psCBuf->Name, StageName, psCBuf->asVars[i].sType.Name); + } +} + +char* GetSamplerTypeMETAL(HLSLCrossCompilerContext* psContext, + const RESOURCE_DIMENSION eDimension, + const uint32_t ui32RegisterNumber, const uint32_t isShadow) +{ + ResourceBinding* psBinding = 0; + RESOURCE_RETURN_TYPE eType = RETURN_TYPE_UNORM; + int found; + found = GetResourceFromBindingPoint(RGROUP_TEXTURE, ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + if (found) + { + eType = (RESOURCE_RETURN_TYPE)psBinding->ui32ReturnType; + } + switch (eDimension) + { + case RESOURCE_DIMENSION_BUFFER: + { + switch (eType) + { + case RETURN_TYPE_SINT: + return ""; + case RETURN_TYPE_UINT: + return ""; + default: + return ""; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE1D: + { + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexture1d"; + case RETURN_TYPE_UINT: + return "\ttexture1d"; + default: + return "\ttexture1d"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2D: + { + if (isShadow) + { + return "\tdepth2d"; + } + + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexture2d"; + case RETURN_TYPE_UINT: + return "\ttexture2d"; + default: + return "\ttexture2d"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2DMS: + { + if (isShadow) + { + return "\tdepth2d_ms"; + } + + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexture2d_ms"; + case RETURN_TYPE_UINT: + return "\ttexture2d_ms"; + default: + return "\ttexture2d_ms"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE3D: + { + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexture3d"; + case RETURN_TYPE_UINT: + return "\ttexture3d"; + default: + return "\ttexture3d"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURECUBE: + { + if (isShadow) + { + return "\tdepthcube"; + } + + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexturecube"; + case RETURN_TYPE_UINT: + return "\ttexturecube"; + default: + return "\ttexturecube"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexture1d_array"; + case RETURN_TYPE_UINT: + return "\ttexture1d_array"; + default: + return "\ttexture1d_array"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + if (isShadow) + { + return "\tdepth2d_array"; + } + + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexture2d_array"; + case RETURN_TYPE_UINT: + return "\ttexture2d_array"; + default: + return "\ttexture2d_array"; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + //Metal does not support this type of resource + ASSERT(0); + switch (eType) + { + case RETURN_TYPE_SINT: + return ""; + case RETURN_TYPE_UINT: + return ""; + default: + return ""; + } + break; + } + + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + switch (eType) + { + case RETURN_TYPE_SINT: + return "\ttexturecube_array"; + case RETURN_TYPE_UINT: + return "\ttexturecube_array"; + default: + return "\ttexturecube_array"; + } + break; + } + } + + return "sampler2D"; +} + +static void TranslateResourceTexture(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, uint32_t samplerCanDoShadowCmp) +{ + bstring metal = *psContext->currentShaderString; + + const char* samplerTypeName = GetSamplerTypeMETAL(psContext, + psDecl->value.eResourceDimension, + psDecl->asOperands[0].ui32RegisterNumber, samplerCanDoShadowCmp && psDecl->ui32IsShadowTex); + + if (samplerCanDoShadowCmp && psDecl->ui32IsShadowTex) + { + //Create shadow and non-shadow sampler. + //HLSL does not have separate types for depth compare, just different functions. + bcatcstr(metal, samplerTypeName); + bcatcstr(metal, " "); + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, 1); + } + else + { + bcatcstr(metal, samplerTypeName); + bcatcstr(metal, " "); + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, 0); + } +} + +void TranslateDeclarationMETAL(HLSLCrossCompilerContext* psContext, const Declaration* psDecl, AtomicVarList* psAtomicList) +{ + bstring metal = *psContext->currentShaderString; + ShaderData* psShader = psContext->psShader; + + switch (psDecl->eOpcode) + { + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_PS_SGV: + { + const SPECIAL_NAME eSpecialName = psDecl->asOperands[0].eSpecialName; + + if (psShader->eShaderType == PIXEL_SHADER) + { + switch (eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinInputMETAL(psContext, psDecl, "position", "float4"); + break; + } + case NAME_CLIP_DISTANCE: + { + AddBuiltinInputMETAL(psContext, psDecl, "clip_distance", "float"); + break; + } + case NAME_INSTANCE_ID: + { + AddBuiltinInputMETAL(psContext, psDecl, "instance_id", "uint"); + break; + } + case NAME_IS_FRONT_FACE: + { + /* + Cast to int used because + if(gl_FrontFacing != 0) failed to compiled on Intel HD 4000. + Suggests no implicit conversion for bool<->int. + */ + + AddBuiltinInputMETAL(psContext, psDecl, "front_facing", "bool"); + break; + } + case NAME_SAMPLE_INDEX: + { + AddBuiltinInputMETAL(psContext, psDecl, "sample_id", "uint"); + break; + } + default: + { + DeclareInput(psContext, psDecl, + "user", OPERAND_MIN_PRECISION_DEFAULT, 4, INDEX_1D, psDecl->asOperands[0].pszSpecialName); + } + } + } + else if (psShader->eShaderType == VERTEX_SHADER) + { + switch (eSpecialName) + { + case NAME_VERTEX_ID: + { + AddBuiltinInputMETAL(psContext, psDecl, "vertex_id", "uint"); + break; + } + case NAME_INSTANCE_ID: + { + AddBuiltinInputMETAL(psContext, psDecl, "instance_id", "uint"); + break; + } + default: + { + DeclareInput(psContext, psDecl, + "attribute", OPERAND_MIN_PRECISION_DEFAULT, 4, INDEX_1D, psDecl->asOperands[0].pszSpecialName); + } + } + } + break; + } + + case OPCODE_DCL_OUTPUT_SIV: + { + switch (psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinOutputMETAL(psContext, psDecl, GLVARTYPE_FLOAT4, 0, "position"); + break; + } + case NAME_CLIP_DISTANCE: + { + AddBuiltinOutputMETAL(psContext, psDecl, GLVARTYPE_FLOAT, 0, "clip_distance"); + break; + } + case NAME_VERTEX_ID: + { + ASSERT(0); //VertexID is not an output + break; + } + case NAME_INSTANCE_ID: + { + ASSERT(0); //InstanceID is not an output + break; + } + case NAME_IS_FRONT_FACE: + { + ASSERT(0); //FrontFacing is not an output + break; + } + default: + { + bformata(metal, "float4 %s;\n", psDecl->asOperands[0].pszSpecialName); + + bcatcstr(metal, "#define "); + TranslateOperandMETAL(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(metal, " %s\n", psDecl->asOperands[0].pszSpecialName); + break; + } + } + break; + } + case OPCODE_DCL_INPUT: + { + const Operand* psOperand = &psDecl->asOperands[0]; + //Force the number of components to be 4. + /*dcl_output o3.xy + dcl_output o3.z + + Would generate a vec2 and a vec3. We discard the second one making .z invalid! + + */ + int iNumComponents = 4;//GetMaxComponentFromComponentMask(psOperand); + const char* InputName; + + if ((psOperand->eType == OPERAND_TYPE_INPUT_DOMAIN_POINT) || + (psOperand->eType == OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID) || + (psOperand->eType == OPERAND_TYPE_INPUT_COVERAGE_MASK) || + (psOperand->eType == OPERAND_TYPE_INPUT_FORK_INSTANCE_ID)) + { + break; + } + if (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID) + { + bformata(psContext->parameterDeclarations, "\tuint3 vThreadID [[ thread_position_in_grid ]],\n"); + break; + } + if (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP) + { + bformata(psContext->parameterDeclarations, "\tuint3 vThreadIDInGroup [[ thread_position_in_threadgroup ]],\n"); + break; + } + if (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_GROUP_ID) + { + bformata(psContext->parameterDeclarations, "\tuint3 vThreadGroupID [[ threadgroup_position_in_grid ]],\n"); + break; + } + if (psOperand->eType == OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED) + { + bformata(psContext->parameterDeclarations, "\tuint vThreadIDInGroupFlattened [[ thread_index_in_threadgroup ]],\n"); + break; + } + //Already declared as part of an array. + if (psShader->aIndexedInput[psDecl->asOperands[0].ui32RegisterNumber] == -1) + { + break; + } + + InputName = GetDeclaredInputNameMETAL(psContext, psShader->eShaderType, psOperand); + + DeclareInput(psContext, psDecl, + "attribute", (OPERAND_MIN_PRECISION)psOperand->eMinPrecision, iNumComponents, (OPERAND_INDEX_DIMENSION)psOperand->iIndexDims, InputName); + + break; + } + case OPCODE_DCL_INPUT_PS_SIV: + { + switch (psDecl->asOperands[0].eSpecialName) + { + case NAME_POSITION: + { + AddBuiltinInputMETAL(psContext, psDecl, "position", "float4"); + break; + } + } + break; + } + case OPCODE_DCL_INPUT_SIV: + { + break; + } + case OPCODE_DCL_INPUT_PS: + { + const Operand* psOperand = &psDecl->asOperands[0]; + int iNumComponents = 4;//GetMaxComponentFromComponentMask(psOperand); + const char* InputName = GetDeclaredInputNameMETAL(psContext, PIXEL_SHADER, psOperand); + + DeclareInput(psContext, psDecl, + "user", (OPERAND_MIN_PRECISION)psOperand->eMinPrecision, iNumComponents, INDEX_1D, InputName); + + break; + } + case OPCODE_DCL_TEMPS: + { + const uint32_t ui32NumTemps = psDecl->value.ui32NumTemps; + + if (ui32NumTemps > 0) + { + bformata(psContext->earlyMain, "\tfloat4 Temp[%d];\n", ui32NumTemps); + + bformata(psContext->earlyMain, "\tint4 Temp_int[%d];\n", ui32NumTemps); + bformata(psContext->earlyMain, "\tuint4 Temp_uint[%d];\n", ui32NumTemps); + bformata(psContext->earlyMain, "\thalf4 Temp_half[%d];\n", ui32NumTemps); + } + + break; + } + case OPCODE_SPECIAL_DCL_IMMCONST: + { + const Operand* psDest = &psDecl->asOperands[0]; + const Operand* psSrc = &psDecl->asOperands[1]; + + ASSERT(psSrc->eType == OPERAND_TYPE_IMMEDIATE32); + if (psDest->eType == OPERAND_TYPE_SPECIAL_IMMCONSTINT) + { + bformata(metal, "const int4 IntImmConst%d = ", psDest->ui32RegisterNumber); + } + else + { + bformata(metal, "const float4 ImmConst%d = ", psDest->ui32RegisterNumber); + AddToDx9ImmConstIndexableArrayMETAL(psContext, psDest); + } + TranslateOperandMETAL(psContext, psSrc, psDest->eType == OPERAND_TYPE_SPECIAL_IMMCONSTINT ? TO_FLAG_INTEGER : TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(metal, ";\n"); + + break; + } + case OPCODE_DCL_CONSTANT_BUFFER: + { + const Operand* psOperand = &psDecl->asOperands[0]; + const uint32_t ui32BindingPoint = psOperand->aui32ArraySizes[0]; + + const char* StageName = "VS"; + + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + StageName = "PS"; + break; + } + case HULL_SHADER: + { + StageName = "HS"; + break; + } + case DOMAIN_SHADER: + { + StageName = "DS"; + break; + } + case GEOMETRY_SHADER: + { + StageName = "GS"; + break; + } + case COMPUTE_SHADER: + { + StageName = "CS"; + break; + } + default: + { + break; + } + } + + ConstantBuffer* psCBuf = NULL; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, ui32BindingPoint, &psContext->psShader->sInfo, &psCBuf); + + if (psCBuf) + { + // Constant buffers declared as "dynamicIndexed" are declared as raw vec4 arrays, as there is no general way to retrieve the member corresponding to a dynamic index. + // Simple cases can probably be handled easily, but for example when arrays (possibly nested with structs) are contained in the constant buffer and the shader reads + // from a dynamic index we would need to "undo" the operations done in order to compute the variable offset, and such a feature is not available at the moment. + psCBuf->blob = psDecl->value.eCBAccessPattern == CONSTANT_BUFFER_ACCESS_PATTERN_DYNAMICINDEXED; + } + + // We don't have a original resource name, maybe generate one??? + if (!psCBuf) + { + bformata(metal, "struct ConstantBuffer%d {\n\tfloat4 data[%d];\n};\n", ui32BindingPoint, psOperand->aui32ArraySizes[1], ui32BindingPoint); + // For vertex shaders HLSLcc generates code that expectes the + // constant buffer to be a pointer. For other shaders it generates + // code that expects a reference instead... + if (psContext->psShader->eShaderType == VERTEX_SHADER) + { + bformata(psContext->parameterDeclarations, "\tconstant ConstantBuffer%d* cb%d [[ buffer(%d) ]],\n", ui32BindingPoint, ui32BindingPoint, ui32BindingPoint); + } + else + { + bformata(psContext->parameterDeclarations, "\tconstant ConstantBuffer%d& cb%d [[ buffer(%d) ]],\n", ui32BindingPoint, ui32BindingPoint, ui32BindingPoint); + } + break; + } + else if (psCBuf->blob) + { + // For vertex shaders HLSLcc generates code that expectes the + // constant buffer to be a pointer. For other shaders it generates + // code that expects a reference instead... + bformata(metal, "struct ConstantBuffer%d {\n\tfloat4 %s[%d];\n};\n", ui32BindingPoint, psCBuf->asVars->Name, psOperand->aui32ArraySizes[1], ui32BindingPoint); + if (psContext->psShader->eShaderType == VERTEX_SHADER) + { + bformata(psContext->parameterDeclarations, "\tconstant ConstantBuffer%d* %s%s_data [[ buffer(%d) ]],\n", ui32BindingPoint, psCBuf->Name, StageName, ui32BindingPoint); + } + else + { + bformata(psContext->parameterDeclarations, "\tconstant ConstantBuffer%d& %s%s_data [[ buffer(%d) ]],\n", ui32BindingPoint, psCBuf->Name, StageName, ui32BindingPoint); + } + break; + } + + DeclareStructConstantsMETAL(psContext, ui32BindingPoint, psCBuf, psOperand, metal, psAtomicList); + + break; + } + case OPCODE_DCL_SAMPLER: + { + if (psDecl->bIsComparisonSampler) + { + psContext->currentShaderString = &psContext->mainShader; + metal = *psContext->currentShaderString; + + bcatcstr(metal, "constexpr sampler "); + ResourceNameMETAL(metal, psContext, RGROUP_SAMPLER, psDecl->asOperands[0].ui32RegisterNumber, 1); + bformata(metal, "(compare_func::less);\n", psDecl->asOperands[0].ui32RegisterNumber); + } + + /* CONFETTI NOTE (DAVID SROUR): + * The following declaration still needs to occur for comparison samplers. + * The Metal layer of the engine will still try to bind a sampler in the appropriate slot. + * This parameter of the shader's entrance function acts as a dummy comparison sampler for the engine. + * Note that 0 is always passed for the "bZCompare" argument of ResourceNameMETAL(...) as to give the dummy + * sampler a different name as the constexpr one. + */ + { + psContext->currentShaderString = &psContext->parameterDeclarations; + metal = *psContext->currentShaderString; + + bcatcstr(metal, "\tsampler "); + ResourceNameMETAL(metal, psContext, RGROUP_SAMPLER, psDecl->asOperands[0].ui32RegisterNumber, 0); + bformata(metal, "[[ sampler(%d) ]],\n", psDecl->asOperands[0].ui32RegisterNumber); + } + break; + } + case OPCODE_DCL_RESOURCE: + { + // CONFETTI BEGIN: David Srour + // METAL PIXEL SHADER RT FETCH + if (psDecl->asOperands[0].ui32RegisterNumber >= GMEM_FLOAT_START_SLOT) + { + int regNum = GetGmemInputResourceSlotMETAL(psDecl->asOperands[0].ui32RegisterNumber); + int numElements = GetGmemInputResourceNumElementsMETAL(psDecl->asOperands[0].ui32RegisterNumber); + + switch (numElements) + { + case 1: + bformata(psContext->parameterDeclarations, "\tfloat"); + break; + case 2: + bformata(psContext->parameterDeclarations, "\tfloat2"); + break; + case 3: + bformata(psContext->parameterDeclarations, "\tfloat3"); + break; + case 4: + bformata(psContext->parameterDeclarations, "\tfloat4"); + break; + default: + bformata(psContext->parameterDeclarations, "\tfloat4"); + break; + } + + psContext->gmemOutputNumElements[regNum] = numElements; + + // Function input framebuffer + bformata(psContext->parameterDeclarations, " GMEM_Input%d [[ color(%d) ]],\n", regNum, regNum); + + break; + } + // CONFETTI END + + psContext->currentShaderString = &psContext->parameterDeclarations; + metal = *psContext->currentShaderString; + + switch (psDecl->value.eResourceDimension) + { + case RESOURCE_DIMENSION_BUFFER: + { + break; + } + case RESOURCE_DIMENSION_TEXTURE1D: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMS: + { + TranslateResourceTexture(psContext, psDecl, 0); + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + TranslateResourceTexture(psContext, psDecl, 0); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + TranslateResourceTexture(psContext, psDecl, 1); + break; + } + } + + bformata(metal, "[[ texture(%d) ]],\n", psDecl->asOperands[0].ui32RegisterNumber); + psContext->currentShaderString = &psContext->mainShader; + metal = *psContext->currentShaderString; + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_TEXTURES); + psShader->aeResourceDims[psDecl->asOperands[0].ui32RegisterNumber] = psDecl->value.eResourceDimension; + break; + } + case OPCODE_DCL_OUTPUT: + { + AddUserOutputMETAL(psContext, psDecl); + break; + } + case OPCODE_DCL_GLOBAL_FLAGS: + { + uint32_t ui32Flags = psDecl->value.ui32GlobalFlags; + + if (ui32Flags & GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL) + { + psContext->needsFragmentTestHint = 1; + } + if (!(ui32Flags & GLOBAL_FLAG_REFACTORING_ALLOWED)) + { + //TODO add precise + //HLSL precise - http://msdn.microsoft.com/en-us/library/windows/desktop/hh447204(v=vs.85).aspx + } + if (ui32Flags & GLOBAL_FLAG_ENABLE_DOUBLE_PRECISION_FLOAT_OPS) + { + // TODO + // Is there something for this in METAL? + } + + break; + } + + case OPCODE_DCL_THREAD_GROUP: + { + /* CONFETTI NOTE: + The thread group information need to be passed to engine side. Add the information + into reflection data. + */ + psContext->psShader->sInfo.ui32Thread_x = psDecl->value.aui32WorkGroupSize[0]; + psContext->psShader->sInfo.ui32Thread_y = psDecl->value.aui32WorkGroupSize[1]; + psContext->psShader->sInfo.ui32Thread_z = psDecl->value.aui32WorkGroupSize[2]; + break; + } + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + { + break; + } + case OPCODE_DCL_TESS_DOMAIN: + { + break; + } + case OPCODE_DCL_TESS_PARTITIONING: + { + break; + } + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + { + break; + } + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + { + break; + } + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + { + break; + } + case OPCODE_DCL_INTERFACE: + { + break; + } + case OPCODE_DCL_FUNCTION_BODY: + { + //bformata(metal, "void Func%d();//%d\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].eType); + break; + } + case OPCODE_DCL_FUNCTION_TABLE: + { + break; + } + case OPCODE_CUSTOMDATA: + { + const uint32_t ui32NumVec4 = psDecl->ui32NumOperands; + const uint32_t ui32NumVec4Minus1 = (ui32NumVec4 - 1); + uint32_t ui32ConstIndex = 0; + float x, y, z, w; + + //If ShaderBitEncodingSupported then 1 integer buffer, use intBitsToFloat to get float values. - More instructions. + //else 2 buffers - one integer and one float. - More data + + if (ShaderBitEncodingSupported(psShader->eTargetLanguage) == 0) + { + bcatcstr(metal, "#define immediateConstBufferI(idx) immediateConstBufferInt[idx]\n"); + bcatcstr(metal, "#define immediateConstBufferF(idx) immediateConstBuffer[idx]\n"); + + bformata(metal, "static constant float4 immediateConstBuffer[%d] = {\n", ui32NumVec4, ui32NumVec4); + for (; ui32ConstIndex < ui32NumVec4Minus1; ui32ConstIndex++) + { + float loopLocalX, loopLocalY, loopLocalZ, loopLocalW; + loopLocalX = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + loopLocalY = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + loopLocalZ = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + loopLocalW = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + + //A single vec4 can mix integer and float types. + //Forced NAN and INF to zero inside the immediate constant buffer. This will allow the shader to compile. + if (fpcheck(loopLocalX)) + { + loopLocalX = 0; + } + if (fpcheck(loopLocalY)) + { + loopLocalY = 0; + } + if (fpcheck(loopLocalZ)) + { + loopLocalZ = 0; + } + if (fpcheck(loopLocalW)) + { + loopLocalW = 0; + } + + bformata(metal, "\tfloat4(%f, %f, %f, %f), \n", loopLocalX, loopLocalY, loopLocalZ, loopLocalW); + } + //No trailing comma on this one + x = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].a; + y = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].b; + z = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].c; + w = *(float*)&psDecl->asImmediateConstBuffer[ui32ConstIndex].d; + if (fpcheck(x)) + { + x = 0; + } + if (fpcheck(y)) + { + y = 0; + } + if (fpcheck(z)) + { + z = 0; + } + if (fpcheck(w)) + { + w = 0; + } + bformata(metal, "\tfloat4(%f, %f, %f, %f)\n", x, y, z, w); + bcatcstr(metal, "};\n"); + } + else + { + bcatcstr(metal, "#define immediateConstBufferI(idx) immediateConstBufferInt[idx]\n"); + bcatcstr(metal, "#define immediateConstBufferF(idx) as_type(immediateConstBufferInt[idx])\n"); + } + + { + uint32_t ui32ConstIndex2 = 0; + int x2, y2, z2, w2; + + bformata(metal, "static constant int4 immediateConstBufferInt[%d] = {\n", ui32NumVec4, ui32NumVec4); + for (; ui32ConstIndex2 < ui32NumVec4Minus1; ui32ConstIndex2++) + { + int loopLocalX, loopLocalY, loopLocalZ, loopLocalW; + loopLocalX = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].a; + loopLocalY = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].b; + loopLocalZ = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].c; + loopLocalW = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].d; + + bformata(metal, "\tint4(%d, %d, %d, %d), \n", loopLocalX, loopLocalY, loopLocalZ, loopLocalW); + } + //No trailing comma on this one + x2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].a; + y2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].b; + z2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].c; + w2 = *(int*)&psDecl->asImmediateConstBuffer[ui32ConstIndex2].d; + + bformata(metal, "\tint4(%d, %d, %d, %d)\n", x2, y2, z2, w2); + bcatcstr(metal, "};\n"); + } + + break; + } + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + { + break; + } + case OPCODE_DCL_INDEXABLE_TEMP: + { + const uint32_t ui32RegIndex = psDecl->sIdxTemp.ui32RegIndex; + const uint32_t ui32RegCount = psDecl->sIdxTemp.ui32RegCount; + const uint32_t ui32RegComponentSize = psDecl->sIdxTemp.ui32RegComponentSize; + bformata(psContext->earlyMain, "float%d TempArray%d[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + bformata(psContext->earlyMain, "int%d TempArray%d_int[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + if (HaveUVec(psShader->eTargetLanguage)) + { + bformata(psContext->earlyMain, "uint%d TempArray%d_uint[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount); + } + break; + } + case OPCODE_DCL_INDEX_RANGE: + { + break; + } + case OPCODE_HS_DECLS: + { + break; + } + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + { + break; + } + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + { + break; + } + case OPCODE_HS_FORK_PHASE: + { + break; + } + case OPCODE_HS_JOIN_PHASE: + { + break; + } + case OPCODE_DCL_HS_MAX_TESSFACTOR: + { + //For metal the max tessellation factor is fixed to the value of gl_MaxTessGenLevel. + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + { + psContext->currentShaderString = &psContext->parameterDeclarations; + metal = *psContext->currentShaderString; + + if (psDecl->value.eResourceDimension == RESOURCE_DIMENSION_BUFFER) + { + { + //give write access + bcatcstr(metal, "\tdevice "); + } + switch (psDecl->sUAV.Type) + { + case RETURN_TYPE_FLOAT: + bcatcstr(metal, "float "); + break; + case RETURN_TYPE_UNORM: + bcatcstr(metal, "TODO: OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED->RETURN_TYPE_UNORM "); + break; + case RETURN_TYPE_SNORM: + bcatcstr(metal, "TODO: OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED->RETURN_TYPE_SNORM "); + break; + case RETURN_TYPE_UINT: + bcatcstr(metal, "uint "); + break; + case RETURN_TYPE_SINT: + bcatcstr(metal, "int "); + break; + default: + ASSERT(0); + } + bstring StructName; + StructName = bfromcstr(""); + ResourceNameMETAL(StructName, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bformata(metal, " * "); + TranslateOperandMETAL(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(metal, " [[buffer(%d)]], \n", psDecl->asOperands[0].ui32RegisterNumber + UAV_BUFFER_START_SLOT); + int count = 0; + for (uint32_t index = 0; index < psContext->psShader->sInfo.ui32NumResourceBindings; index++) + { + if (strcmp(psContext->psShader->sInfo.psResourceBindings[index].Name, (const char*)StructName->data) == 0) + { + count++; + //psContext->psShader->sInfo.psResourceBindings[index].ui32BindPoint += UAV_BUFFER_START_SLOT; + psContext->psShader->sInfo.psResourceBindings[index].eBindArea = UAVAREA_CBUFFER; + } + } + //If count >2, the logic here is wrong and need to be modified. + ASSERT(count < 2); + } + else + { + switch (psDecl->value.eResourceDimension) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + bformata(metal, "\ttexture1d<"); + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + bformata(metal, "\ttexture2d<"); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMS: + { + //metal does not support this + ASSERT(0); + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + bformata(metal, "\ttexture3d<"); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + bformata(metal, "\ttexturecube<"); + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + bformata(metal, "\ttexture1d_array<"); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + bformata(metal, "\ttexture2d_array<"); + break; + } + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + { + //metal does not suuport this. + ASSERT(0); + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + bformata(metal, "\ttexturecube_array<"); + break; + } + } + switch (psDecl->sUAV.Type) + { + case RETURN_TYPE_FLOAT: + bcatcstr(metal, "float "); + break; + case RETURN_TYPE_UNORM: + bcatcstr(metal, "TODO: OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED->RETURN_TYPE_UNORM "); + break; + case RETURN_TYPE_SNORM: + bcatcstr(metal, "TODO: OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED->RETURN_TYPE_SNORM "); + break; + case RETURN_TYPE_UINT: + bcatcstr(metal, "uint "); + break; + case RETURN_TYPE_SINT: + bcatcstr(metal, "int "); + break; + default: + ASSERT(0); + } + if (psShader->aiOpcodeUsed[OPCODE_STORE_UAV_TYPED] == 0) + { + bcatcstr(metal, "> "); + } + else + { + //give write access + bcatcstr(metal, ", access::write> "); + } + bstring StructName; + StructName = bfromcstr(""); + ResourceNameMETAL(StructName, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + TranslateOperandMETAL(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(metal, " [[texture(%d)]], \n", psDecl->asOperands[0].ui32RegisterNumber + UAV_BUFFER_START_SLOT); + int count = 0; + for (uint32_t index = 0; index < psContext->psShader->sInfo.ui32NumResourceBindings; index++) + { + if (strcmp(psContext->psShader->sInfo.psResourceBindings[index].Name, (const char*)StructName->data) == 0) + { + count++; + //psContext->psShader->sInfo.psResourceBindings[index].ui32BindPoint += UAV_BUFFER_START_SLOT; + psContext->psShader->sInfo.psResourceBindings[index].eBindArea = UAVAREA_TEXTURE; + } + } + //If count >2, the logic here is wrong and need to be modified. + ASSERT(count < 2); + //TranslateOperand(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + } + psContext->currentShaderString = &psContext->mainShader; + metal = *psContext->currentShaderString; + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + { + const uint32_t ui32BindingPoint = psDecl->asOperands[0].aui32ArraySizes[0]; + ConstantBuffer* psCBuf = NULL; + + if (psDecl->sUAV.bCounter) + { + bformata(metal, "atomic_uint "); + ResourceNameMETAL(metal, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bformata(metal, "_counter; \n"); + } + + GetConstantBufferFromBindingPoint(RGROUP_UAV, ui32BindingPoint, &psContext->psShader->sInfo, &psCBuf); + + DeclareBufferVariableMETAL(psContext, ui32BindingPoint, psCBuf, &psDecl->asOperands[0], RTYPE_UAV_RWSTRUCTURED, metal, psAtomicList); + break; + } + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + { + if (psDecl->sUAV.bCounter) + { + bformata(metal, "atomic_uint "); + ResourceNameMETAL(metal, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bformata(metal, "_counter; \n"); + } + + bformata(metal, "buffer Block%d {\n\tuint ", psDecl->asOperands[0].ui32RegisterNumber); + ResourceNameMETAL(metal, psContext, RGROUP_UAV, psDecl->asOperands[0].ui32RegisterNumber, 0); + bcatcstr(metal, "[];\n};\n"); + + break; + } + case OPCODE_DCL_RESOURCE_STRUCTURED: + { + ConstantBuffer* psCBuf = NULL; + + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psDecl->asOperands[0].ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + DeclareBufferVariableMETAL(psContext, psDecl->asOperands[0].ui32RegisterNumber, psCBuf, &psDecl->asOperands[0], + RTYPE_STRUCTURED, psContext->mainShader, psAtomicList); + break; + } + case OPCODE_DCL_RESOURCE_RAW: + { + bformata(metal, "buffer Block%d {\n\tuint RawRes%d[];\n};\n", psDecl->asOperands[0].ui32RegisterNumber, psDecl->asOperands[0].ui32RegisterNumber); + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + { + psContext->currentShaderString = &psContext->earlyMain; + metal = *psContext->currentShaderString; + + ShaderVarType* psVarType = &psShader->sGroupSharedVarType[psDecl->asOperands[0].ui32RegisterNumber]; + + ASSERT(psDecl->asOperands[0].ui32RegisterNumber < MAX_GROUPSHARED); + + bcatcstr(metal, "\tthreadgroup struct {\n"); + bformata(metal, "\t\tuint value[%d];\n", psDecl->sTGSM.ui32Stride / 4); + bcatcstr(metal, "\t} "); + TranslateOperandMETAL(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(metal, "[%d];\n", + psDecl->sTGSM.ui32Count); + + memset(psVarType, 0, sizeof(ShaderVarType)); + strcpy(psVarType->Name, "$Element"); + + psVarType->Columns = psDecl->sTGSM.ui32Stride / 4; + psVarType->Elements = psDecl->sTGSM.ui32Count; + + psContext->currentShaderString = &psContext->mainShader; + metal = *psContext->currentShaderString; + break; + } + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW: + { +#ifdef _DEBUG + //AddIndentation(psContext); + //bcatcstr(metal, "//TODO: OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW\n"); +#endif + psContext->currentShaderString = &psContext->earlyMain; + metal = *psContext->currentShaderString; + bcatcstr(metal, "\tthreadgroup "); + bformata(metal, "atomic_uint "); + //psDecl->asOperands + TranslateOperandMETAL(psContext, &psDecl->asOperands[0], TO_FLAG_NONE); + bformata(metal, "[%d]; \n", psDecl->sTGSM.ui32Stride / 4); + + psContext->currentShaderString = &psContext->mainShader; + metal = *psContext->currentShaderString; + break; + } + case OPCODE_DCL_STREAM: + { + break; + } + case OPCODE_DCL_GS_INSTANCE_COUNT: + { + break; + } + default: + { + ASSERT(0); + break; + } + } +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALInstruction.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALInstruction.c new file mode 100644 index 0000000000..547f293733 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALInstruction.c @@ -0,0 +1,4946 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toMETALInstruction.h" +#include "internal_includes/toMETALOperand.h" +#include "internal_includes/languages.h" +#include "bstrlib.h" +#include "stdio.h" +#include +#include "hlslcc.h" +#include +#include "internal_includes/debug.h" + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); +static int METALIsIntegerImmediateOpcode(OPCODE_TYPE eOpcode); + +// Calculate the bits set in mask +static int METALWriteMaskToComponentCount(uint32_t writeMask) +{ + uint32_t count; + // In HLSL bytecode writemask 0 also means everything + if (writeMask == 0) + { + return 4; + } + + // Count bits set + // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64 + count = (writeMask * 0x200040008001ULL & 0x111111111111111ULL) % 0xf; + + return (int)count; +} + +static uint32_t METALBuildComponentMaskFromElementCount(int count) +{ + // Translate numComponents into bitmask + // 1 -> 1, 2 -> 3, 3 -> 7 and 4 -> 15 + return (1 << count) - 1; +} + + +// This function prints out the destination name, possible destination writemask, assignment operator +// and any possible conversions needed based on the eSrcType+ui32SrcElementCount (type and size of data expected to be coming in) +// As an output, pNeedsParenthesis will be filled with the amount of closing parenthesis needed +// and pSrcCount will be filled with the number of components expected +// ui32CompMask can be used to only write to 1 or more components (used by MOVC) +static void METALAddOpAssignToDestWithMask(HLSLCrossCompilerContext* psContext, const Operand* psDest, + SHADER_VARIABLE_TYPE eSrcType, uint32_t ui32SrcElementCount, const char* szAssignmentOp, int* pNeedsParenthesis, uint32_t ui32CompMask) +{ + uint32_t ui32DestElementCount = GetNumSwizzleElementsWithMaskMETAL(psDest, ui32CompMask); + bstring metal = *psContext->currentShaderString; + SHADER_VARIABLE_TYPE eDestDataType = GetOperandDataTypeMETAL(psContext, psDest); + ASSERT(pNeedsParenthesis != NULL); + + *pNeedsParenthesis = 0; + + uint32_t flags = TO_FLAG_DESTINATION; + // Default is full floats. Handle half floats if the source is half precision + if (eSrcType == SVT_FLOAT16) + { + flags |= TO_FLAG_FLOAT16; + } + TranslateOperandWithMaskMETAL(psContext, psDest, flags, ui32CompMask); + + //GMEM data output types can only be full floats. + if(eDestDataType== SVT_FLOAT16 && psDest->eType== OPERAND_TYPE_OUTPUT && psContext->gmemOutputNumElements[0]>0 ) + { + eDestDataType = SVT_FLOAT; + } + + // Simple path: types match. + if (eDestDataType == eSrcType) + { + // Cover cases where the HLSL language expects the rest of the components to be default-filled + // eg. MOV r0, c0.x => Temp[0] = vec4(c0.x); + if (ui32DestElementCount > ui32SrcElementCount) + { + bformata(metal, " %s %s(", szAssignmentOp, GetConstructorForTypeMETAL(eDestDataType, ui32DestElementCount)); + *pNeedsParenthesis = 1; + } + else + { + bformata(metal, " %s ", szAssignmentOp); + } + return; + } + + switch (eDestDataType) + { + case SVT_INT: + { + if (1 == ui32DestElementCount) + { + bformata(metal, " %s as_type(", szAssignmentOp); + } + else + { + bformata(metal, "%s as_type(", szAssignmentOp, ui32DestElementCount); + } + break; + } + case SVT_UINT: + { + if (1 == ui32DestElementCount) + { + bformata(metal, " %s as_type(", szAssignmentOp); + } + else + { + bformata(metal, "%s as_type(", szAssignmentOp, ui32DestElementCount); + } + break; + } + case SVT_FLOAT: + { + const char* castType = eSrcType == SVT_FLOAT16 ? "static_cast" : "as_type"; + if (1 == ui32DestElementCount) + { + bformata(metal, " %s %s(", szAssignmentOp, castType); + } + else + { + bformata(metal, "%s %s(", szAssignmentOp, castType, ui32DestElementCount); + } + break; + } + case SVT_FLOAT16: + { + if (1 == ui32DestElementCount) + { + bformata(metal, " %s static_cast(", szAssignmentOp); + } + else + { + bformata(metal, "%s static_cast(", szAssignmentOp, ui32DestElementCount); + } + break; + } + default: + // TODO: Handle bools? + break; + } + + switch (eDestDataType) + { + case SVT_INT: + case SVT_UINT: + case SVT_FLOAT: + case SVT_FLOAT16: + { + // Cover cases where the HLSL language expects the rest of the components to be default-filled + if (ui32DestElementCount > ui32SrcElementCount) + { + bformata(metal, "%s(", GetConstructorForTypeMETAL(eSrcType, ui32DestElementCount)); + (*pNeedsParenthesis)++; + } + } + } + (*pNeedsParenthesis)++; + return; +} + +static void METALAddAssignToDest(HLSLCrossCompilerContext* psContext, const Operand* psDest, + SHADER_VARIABLE_TYPE eSrcType, uint32_t ui32SrcElementCount, int* pNeedsParenthesis) +{ + METALAddOpAssignToDestWithMask(psContext, psDest, eSrcType, ui32SrcElementCount, "=", pNeedsParenthesis, OPERAND_4_COMPONENT_MASK_ALL); +} + +static void METALAddAssignPrologue(HLSLCrossCompilerContext* psContext, int numParenthesis) +{ + bstring glsl = *psContext->currentShaderString; + while (numParenthesis != 0) + { + bcatcstr(glsl, ")"); + numParenthesis--; + } + bcatcstr(glsl, ";\n"); +} +static uint32_t METALResourceReturnTypeToFlag(const RESOURCE_RETURN_TYPE eType) +{ + if (eType == RETURN_TYPE_SINT) + { + return TO_FLAG_INTEGER; + } + else if (eType == RETURN_TYPE_UINT) + { + return TO_FLAG_UNSIGNED_INTEGER; + } + else + { + return TO_FLAG_NONE; + } +} + + +typedef enum +{ + METAL_CMP_EQ, + METAL_CMP_LT, + METAL_CMP_GE, + METAL_CMP_NE, +} METALComparisonType; + +static void METALAddComparision(HLSLCrossCompilerContext* psContext, Instruction* psInst, METALComparisonType eType, + uint32_t typeFlag, Instruction* psNextInst) +{ + (void)psNextInst; + + // Multiple cases to consider here: + // For shader model <=3: all comparisons are floats + // otherwise: + // OPCODE_LT, _GT, _NE etc: inputs are floats, outputs UINT 0xffffffff or 0. typeflag: TO_FLAG_NONE + // OPCODE_ILT, _IGT etc: comparisons are signed ints, outputs UINT 0xffffffff or 0 typeflag TO_FLAG_INTEGER + // _ULT, UGT etc: inputs unsigned ints, outputs UINTs typeflag TO_FLAG_UNSIGNED_INTEGER + // + // Additional complexity: if dest swizzle element count is 1, we can use normal comparison operators, otherwise glsl intrinsics. + + uint32_t orig_type = typeFlag; + + bstring metal = *psContext->currentShaderString; + const uint32_t destElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[1]); + const uint32_t s1ElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[2]); + + uint32_t minElemCount = destElemCount < s0ElemCount ? destElemCount : s0ElemCount; + + int needsParenthesis = 0; + + ASSERT(s0ElemCount == s1ElemCount || s1ElemCount == 1 || s0ElemCount == 1); + if (s0ElemCount != s1ElemCount) + { + // Set the proper auto-expand flag is either argument is scalar + typeFlag |= (TO_AUTO_EXPAND_TO_VEC2 << (max(s0ElemCount, s1ElemCount) - 2)); + } + + const char* metalOpcode[] = { + "==", + "<", + ">=", + "!=", + }; + + //Scalar compare + + // Optimization shortcut for the IGE+BREAKC_NZ combo: + // First print out the if(cond)->break directly, and then + // to guarantee correctness with side-effects, re-run + // the actual comparison. In most cases, the second run will + // be removed by the shader compiler optimizer pass (dead code elimination) + // This also makes it easier for some GLSL optimizers to recognize the for loop. + + //if (psInst->eOpcode == OPCODE_IGE && + // psNextInst && + // psNextInst->eOpcode == OPCODE_BREAKC && + // (psInst->asOperands[0].ui32RegisterNumber == psNextInst->asOperands[0].ui32RegisterNumber)) + //{ + + // AddIndentation(psContext); + // bcatcstr(glsl, "// IGE+BREAKC opt\n"); + // AddIndentation(psContext); + + // if (psNextInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO) + // bcatcstr(glsl, "if (("); + // else + // bcatcstr(glsl, "if (!("); + // TranslateOperand(psContext, &psInst->asOperands[1], typeFlag); + // bformata(glsl, "%s ", glslOpcode[eType]); + // TranslateOperand(psContext, &psInst->asOperands[2], typeFlag); + // bcatcstr(glsl, ")) { break; }\n"); + + // // Mark the BREAKC instruction as already handled + // psNextInst->eOpcode = OPCODE_NOP; + + // // Continue as usual + //} + + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_INT, destElemCount, &needsParenthesis); + + bcatcstr(metal, "select("); + + /* Confetti note: + ASM returns 0XFFFFFFFF or 0 + It's important to use int. + A sign intrinsic converts to the following: + lt r0.x, l(0.000000), v0.z + lt r0.y, v0.z, l(0.000000) + iadd r0.x, -r0.x, r0.y + itof o0.xyzw, r0.xxxx + */ + + if (destElemCount == 1) + { + bcatcstr(metal, "0, (int)0xFFFFFFFF, ("); + } + else + { + bformata(metal, "int%d(0), int%d(0xFFFFFFFF), (", destElemCount, destElemCount); + } + + TranslateOperandMETAL(psContext, &psInst->asOperands[1], typeFlag); + bcatcstr(metal, ")"); + if (destElemCount > 1) + { + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + } + else if (s0ElemCount > minElemCount) + { + AddSwizzleUsingElementCountMETAL(psContext, minElemCount); + } + bformata(metal, " %s (", metalOpcode[eType]); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], typeFlag); + bcatcstr(metal, ")"); + if (destElemCount > 1) + { + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + } + else if (s1ElemCount > minElemCount || orig_type != typeFlag) + { + AddSwizzleUsingElementCountMETAL(psContext, minElemCount); + } + bcatcstr(metal, ")"); + METALAddAssignPrologue(psContext, needsParenthesis); +} + + +static void METALAddMOVBinaryOp(HLSLCrossCompilerContext* psContext, const Operand* pDest, Operand* pSrc) +{ + int numParenthesis = 0; + int srcSwizzleCount = GetNumSwizzleElementsMETAL(pSrc); + uint32_t writeMask = GetOperandWriteMaskMETAL(pDest); + + const SHADER_VARIABLE_TYPE eSrcType = GetOperandDataTypeExMETAL(psContext, pSrc, GetOperandDataTypeMETAL(psContext, pDest)); + uint32_t flags = SVTTypeToFlagMETAL(eSrcType); + + METALAddAssignToDest(psContext, pDest, eSrcType, srcSwizzleCount, &numParenthesis); + TranslateOperandWithMaskMETAL(psContext, pSrc, flags, writeMask); + + METALAddAssignPrologue(psContext, numParenthesis); +} + +static uint32_t METALElemCountToAutoExpandFlag(uint32_t elemCount) +{ + return TO_AUTO_EXPAND_TO_VEC2 << (elemCount - 2); +} + +static void METALAddMOVCBinaryOp(HLSLCrossCompilerContext* psContext, const Operand* pDest, const Operand* src0, Operand* src1, Operand* src2) +{ + bstring metal = *psContext->currentShaderString; + uint32_t destElemCount = GetNumSwizzleElementsMETAL(pDest); + uint32_t s0ElemCount = GetNumSwizzleElementsMETAL(src0); + uint32_t s1ElemCount = GetNumSwizzleElementsMETAL(src1); + uint32_t s2ElemCount = GetNumSwizzleElementsMETAL(src2); + uint32_t destWriteMask = GetOperandWriteMaskMETAL(pDest); + uint32_t destElem; + + const SHADER_VARIABLE_TYPE eDestType = GetOperandDataTypeMETAL(psContext, pDest); + /* + for each component in dest[.mask] + if the corresponding component in src0 (POS-swizzle) + has any bit set + { + copy this component (POS-swizzle) from src1 into dest + } + else + { + copy this component (POS-swizzle) from src2 into dest + } + endfor + */ + + /* Single-component conditional variable (src0) */ + if (s0ElemCount == 1 || IsSwizzleReplicatedMETAL(src0)) + { + int numParenthesis = 0; + AddIndentation(psContext); + + bcatcstr(metal, "if ("); + TranslateOperandMETAL(psContext, src0, TO_AUTO_BITCAST_TO_INT); + if (s0ElemCount > 1) + { + bcatcstr(metal, ".x"); + } + + bcatcstr(metal, " != 0)\n"); + AddIndentation(psContext); + AddIndentation(psContext); + + METALAddAssignToDest(psContext, pDest, eDestType, destElemCount, &numParenthesis); + + if (s1ElemCount == 1 && destElemCount > 1) + { + TranslateOperandMETAL(psContext, src1, SVTTypeToFlagMETAL(eDestType) | METALElemCountToAutoExpandFlag(destElemCount)); + } + else + { + TranslateOperandWithMaskMETAL(psContext, src1, SVTTypeToFlagMETAL(eDestType), destWriteMask); + } + + bcatcstr(metal, ";\n"); + AddIndentation(psContext); + bcatcstr(metal, "else\n"); + AddIndentation(psContext); + AddIndentation(psContext); + + METALAddAssignToDest(psContext, pDest, eDestType, destElemCount, &numParenthesis); + + if (s2ElemCount == 1 && destElemCount > 1) + { + TranslateOperandMETAL(psContext, src2, SVTTypeToFlagMETAL(eDestType) | METALElemCountToAutoExpandFlag(destElemCount)); + } + else + { + TranslateOperandWithMaskMETAL(psContext, src2, SVTTypeToFlagMETAL(eDestType), destWriteMask); + } + + METALAddAssignPrologue(psContext, numParenthesis); + } + else + { + // TODO: We can actually do this in one op using mix(). + int srcElem = 0; + for (destElem = 0; destElem < 4; ++destElem) + { + int numParenthesis = 0; + if (pDest->eSelMode == OPERAND_4_COMPONENT_MASK_MODE && pDest->ui32CompMask != 0 && !(pDest->ui32CompMask & (1 << destElem))) + { + continue; + } + + AddIndentation(psContext); + + bcatcstr(metal, "if ("); + TranslateOperandWithMaskMETAL(psContext, src0, TO_AUTO_BITCAST_TO_INT, 1 << destElem); + bcatcstr(metal, " != 0)\n"); + + AddIndentation(psContext); + AddIndentation(psContext); + + METALAddOpAssignToDestWithMask(psContext, pDest, eDestType, 1, "=", &numParenthesis, 1 << destElem); + + TranslateOperandWithMaskMETAL(psContext, src1, SVTTypeToFlagMETAL(eDestType), 1 << destElem); + + bcatcstr(metal, ";\n"); + AddIndentation(psContext); + bcatcstr(metal, "else\n"); + AddIndentation(psContext); + AddIndentation(psContext); + + METALAddOpAssignToDestWithMask(psContext, pDest, eDestType, 1, "=", &numParenthesis, 1 << destElem); + TranslateOperandWithMaskMETAL(psContext, src2, SVTTypeToFlagMETAL(eDestType), 1 << destElem); + + METALAddAssignPrologue(psContext, numParenthesis); + + srcElem++; + } + } +} + +// Returns nonzero if operands are identical, only cares about temp registers currently. +static int METALAreTempOperandsIdentical(const Operand* psA, const Operand* psB) +{ + if (!psA || !psB) + { + return 0; + } + + if (psA->eType != OPERAND_TYPE_TEMP || psB->eType != OPERAND_TYPE_TEMP) + { + return 0; + } + + if (psA->eModifier != psB->eModifier) + { + return 0; + } + + if (psA->iNumComponents != psB->iNumComponents) + { + return 0; + } + + if (psA->ui32RegisterNumber != psB->ui32RegisterNumber) + { + return 0; + } + + if (psA->eSelMode != psB->eSelMode) + { + return 0; + } + + if (psA->eSelMode == OPERAND_4_COMPONENT_MASK_MODE && psA->ui32CompMask != psB->ui32CompMask) + { + return 0; + } + + if (psA->eSelMode != OPERAND_4_COMPONENT_MASK_MODE && psA->ui32Swizzle != psB->ui32Swizzle) + { + return 0; + } + + return 1; +} + +// Returns nonzero if the operation is commutative +static int METALIsOperationCommutative(OPCODE_TYPE eOpCode) +{ + switch (eOpCode) + { + case OPCODE_DADD: + case OPCODE_IADD: + case OPCODE_ADD: + case OPCODE_MUL: + case OPCODE_IMUL: + case OPCODE_OR: + case OPCODE_AND: + return 1; + default: + return 0; + } +} + +static void METALCallBinaryOp(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, SHADER_VARIABLE_TYPE eDataType) +{ + bstring glsl = *psContext->currentShaderString; + uint32_t src1SwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[src1]); + uint32_t src0SwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[src0]); + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + uint32_t destMask = GetOperandWriteMaskMETAL(&psInst->asOperands[dest]); + int needsParenthesis = 0; + + AddIndentation(psContext); + + if (src1SwizCount == src0SwizCount == dstSwizCount) + { + // Optimization for readability (and to make for loops in WebGL happy): detect cases where either src == dest and emit +=, -= etc. instead. + if (METALAreTempOperandsIdentical(&psInst->asOperands[dest], &psInst->asOperands[src0]) != 0) + { + METALAddOpAssignToDestWithMask(psContext, &psInst->asOperands[dest], eDataType, dstSwizCount, name, &needsParenthesis, OPERAND_4_COMPONENT_MASK_ALL); + TranslateOperandMETAL(psContext, &psInst->asOperands[src1], SVTTypeToFlagMETAL(eDataType)); + METALAddAssignPrologue(psContext, needsParenthesis); + return; + } + else if (METALAreTempOperandsIdentical(&psInst->asOperands[dest], &psInst->asOperands[src1]) != 0 && (METALIsOperationCommutative(psInst->eOpcode) != 0)) + { + METALAddOpAssignToDestWithMask(psContext, &psInst->asOperands[dest], eDataType, dstSwizCount, name, &needsParenthesis, OPERAND_4_COMPONENT_MASK_ALL); + TranslateOperandMETAL(psContext, &psInst->asOperands[src0], SVTTypeToFlagMETAL(eDataType)); + METALAddAssignPrologue(psContext, needsParenthesis); + return; + } + } + + METALAddAssignToDest(psContext, &psInst->asOperands[dest], eDataType, dstSwizCount, &needsParenthesis); + + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], SVTTypeToFlagMETAL(eDataType), destMask); + bformata(glsl, " %s ", name); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src1], SVTTypeToFlagMETAL(eDataType), destMask); + METALAddAssignPrologue(psContext, needsParenthesis); +} + +static void METALCallTernaryOp(HLSLCrossCompilerContext* psContext, const char* op1, const char* op2, Instruction* psInst, + int dest, int src0, int src1, int src2, uint32_t dataType) +{ + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + uint32_t destMask = GetOperandWriteMaskMETAL(&psInst->asOperands[dest]); + + const SHADER_VARIABLE_TYPE eDestType = GetOperandDataTypeMETAL(psContext, &psInst->asOperands[dest]); + uint32_t ui32Flags = dataType | SVTTypeToFlagMETAL(eDestType); + int numParenthesis = 0; + + AddIndentation(psContext); + + METALAddAssignToDest(psContext, &psInst->asOperands[dest], TypeFlagsToSVTTypeMETAL(dataType), dstSwizCount, &numParenthesis); + + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bformata(glsl, " %s ", op1); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + bformata(glsl, " %s ", op2); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src2], ui32Flags, destMask); + METALAddAssignPrologue(psContext, numParenthesis); +} + +static void METALCallHelper3(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, int src2, int paramsShouldFollowWriteMask) +{ + const SHADER_VARIABLE_TYPE eDestType = GetOperandDataTypeMETAL(psContext, &psInst->asOperands[dest]); + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_FLOAT | SVTTypeToFlagMETAL(eDestType); + + bstring glsl = *psContext->currentShaderString; + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMaskMETAL(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + int numParenthesis = 0; + + + AddIndentation(psContext); + + METALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_FLOAT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src2], ui32Flags, destMask); + METALAddAssignPrologue(psContext, numParenthesis); +} + +static void METALCallHelper2(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, int paramsShouldFollowWriteMask) +{ + const SHADER_VARIABLE_TYPE eDestType = GetOperandDataTypeMETAL(psContext, &psInst->asOperands[dest]); + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_FLOAT | SVTTypeToFlagMETAL(eDestType); + + bstring glsl = *psContext->currentShaderString; + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMaskMETAL(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + + int isDotProduct = (strncmp(name, "dot", 3) == 0) ? 1 : 0; + int numParenthesis = 0; + + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_FLOAT, isDotProduct ? 1 : dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + + METALAddAssignPrologue(psContext, numParenthesis); +} + +static void METALCallHelper2Int(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_INT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMaskMETAL(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + METALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_INT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + METALAddAssignPrologue(psContext, numParenthesis); +} + +static void METALCallHelper2UInt(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int src1, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_UINT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMaskMETAL(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + METALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_UINT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + bcatcstr(glsl, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src1], ui32Flags, destMask); + METALAddAssignPrologue(psContext, numParenthesis); +} + +static void METALCallHelper1(HLSLCrossCompilerContext* psContext, const char* name, Instruction* psInst, + int dest, int src0, int paramsShouldFollowWriteMask) +{ + uint32_t ui32Flags = TO_AUTO_BITCAST_TO_FLOAT; + bstring glsl = *psContext->currentShaderString; + uint32_t dstSwizCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[dest]); + uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMaskMETAL(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; + int numParenthesis = 0; + + AddIndentation(psContext); + + METALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_FLOAT, dstSwizCount, &numParenthesis); + + bformata(glsl, "%s(", name); + numParenthesis++; + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[src0], ui32Flags, destMask); + METALAddAssignPrologue(psContext, numParenthesis); +} + +////Result is an int. +//static void METALCallHelper1Int(HLSLCrossCompilerContext* psContext, +// const char* name, +// Instruction* psInst, +// const int dest, +// const int src0, +// int paramsShouldFollowWriteMask) +//{ +// uint32_t ui32Flags = TO_AUTO_BITCAST_TO_INT; +// bstring glsl = *psContext->currentShaderString; +// uint32_t src0SwizCount = GetNumSwizzleElements(&psInst->asOperands[src0]); +// uint32_t dstSwizCount = GetNumSwizzleElements(&psInst->asOperands[dest]); +// uint32_t destMask = paramsShouldFollowWriteMask ? GetOperandWriteMask(&psInst->asOperands[dest]) : OPERAND_4_COMPONENT_MASK_ALL; +// int numParenthesis = 0; +// +// AddIndentation(psContext); +// +// METALAddAssignToDest(psContext, &psInst->asOperands[dest], SVT_INT, dstSwizCount, &numParenthesis); +// +// bformata(glsl, "%s(", name); +// numParenthesis++; +// TranslateOperandWithMask(psContext, &psInst->asOperands[src0], ui32Flags, destMask); +// METALAddAssignPrologue(psContext, numParenthesis); +//} + +static void METALTranslateTexelFetch(HLSLCrossCompilerContext* psContext, + Instruction* psInst, + ResourceBinding* psBinding, + bstring metal) +{ + int numParenthesis = 0; + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], TypeFlagsToSVTTypeMETAL(METALResourceReturnTypeToFlag(psBinding->ui32ReturnType)), 4, &numParenthesis); + + switch (psBinding->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + { + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ".read("); + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").x)"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + bcatcstr(metal, ")"); + + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ".read("); + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").x, ("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").y)"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + bcatcstr(metal, ")"); + + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + { + // METAL PIXEL SHADER RT FETCH + if (psInst->asOperands[2].ui32RegisterNumber >= GMEM_FLOAT_START_SLOT) + { + bformata(metal, "(GMEM_Input%d", GetGmemInputResourceSlotMETAL(psInst->asOperands[2].ui32RegisterNumber)); + + int gmemNumElements = GetGmemInputResourceNumElementsMETAL(psInst->asOperands[2].ui32RegisterNumber); + + int destNumElements = 0; + + if (psInst->asOperands[0].iNumComponents != 1) + { + //Component Mask + uint32_t mask = psInst->asOperands[0].ui32CompMask; + + if (mask == OPERAND_4_COMPONENT_MASK_ALL) + { + destNumElements = 4; + } + else if (mask != 0) + { + if (mask & OPERAND_4_COMPONENT_MASK_X) + { + destNumElements++; + } + if (mask & OPERAND_4_COMPONENT_MASK_Y) + { + destNumElements++; + } + if (mask & OPERAND_4_COMPONENT_MASK_Z) + { + destNumElements++; + } + if (mask & OPERAND_4_COMPONENT_MASK_W) + { + destNumElements++; + } + } + } + else + { + destNumElements = 4; + } + + TranslateGmemOperandSwizzleWithMaskMETAL(psContext, &psInst->asOperands[2], OPERAND_4_COMPONENT_MASK_ALL, gmemNumElements); + bcatcstr(metal, ")"); + + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + } + else + { + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ".read("); + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").xy, ("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").w)"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + bcatcstr(metal, ")"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + } + + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ".read("); + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").xy, ("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").z, ("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").w)"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + bcatcstr(metal, ")"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + { + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ".read("); + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").xyz, ("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").w)"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + bcatcstr(metal, ")"); + + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: + { + ASSERT(psInst->eOpcode == OPCODE_LD_MS); + + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ".read("); + + bcatcstr(metal, "("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ").xy, "); + TranslateOperandMETAL(psContext, &psInst->asOperands[3], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ")"); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[0]); + + break; + } + case REFLECT_RESOURCE_DIMENSION_BUFFER: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + case REFLECT_RESOURCE_DIMENSION_BUFFEREX: + default: + { + ASSERT(0); + break; + } + } + + METALAddAssignPrologue(psContext, numParenthesis); +} + +//static void METALTranslateTexelFetchOffset(HLSLCrossCompilerContext* psContext, +// Instruction* psInst, +// ResourceBinding* psBinding, +// bstring metal) +//{ +// int numParenthesis = 0; +// uint32_t destCount = GetNumSwizzleElements(&psInst->asOperands[0]); +// AddIndentation(psContext); +// METALAddAssignToDest(psContext, &psInst->asOperands[0], TypeFlagsToSVTType(METALResourceReturnTypeToFlag(psBinding->ui32ReturnType)), 4, &numParenthesis); +// +// bcatcstr(metal, "texelFetchOffset("); +// +// switch (psBinding->eDimension) +// { +// case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: +// { +// TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); +// bcatcstr(metal, ", "); +// TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); +// bformata(metal, ", 0, %d)", psInst->iUAddrOffset); +// break; +// } +// case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: +// { +// TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); +// bcatcstr(metal, ", "); +// TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC3, 7 /* .xyz */); +// bformata(metal, ", 0, int2(%d, %d))", +// psInst->iUAddrOffset, +// psInst->iVAddrOffset); +// break; +// } +// case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: +// { +// TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); +// bcatcstr(metal, ", "); +// TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC3, 7 /* .xyz */); +// bformata(metal, ", 0, int3(%d, %d, %d))", +// psInst->iUAddrOffset, +// psInst->iVAddrOffset, +// psInst->iWAddrOffset); +// break; +// } +// case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: +// { +// TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); +// bcatcstr(metal, ", "); +// TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC2, 3 /* .xy */); +// bformata(metal, ", 0, int2(%d, %d))", psInst->iUAddrOffset, psInst->iVAddrOffset); +// break; +// } +// case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: +// { +// TranslateOperand(psContext, &psInst->asOperands[2], TO_FLAG_NONE); +// bcatcstr(metal, ", "); +// TranslateOperandWithMask(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER | TO_AUTO_EXPAND_TO_VEC2, 3 /* .xy */); +// bformata(metal, ", 0, int(%d))", psInst->iUAddrOffset); +// break; +// } +// case REFLECT_RESOURCE_DIMENSION_BUFFER: +// case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: +// case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: +// case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: +// case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: +// case REFLECT_RESOURCE_DIMENSION_BUFFEREX: +// default: +// { +// ASSERT(0); +// break; +// } +// } +// +// AddSwizzleUsingElementCount(psContext, destCount); +// METALAddAssignPrologue(psContext, numParenthesis); +//} + + +//Makes sure the texture coordinate swizzle is appropriate for the texture type. +//i.e. vecX for X-dimension texture. +//Currently supports floating point coord only, so not used for texelFetch. +static void METALTranslateTexCoord(HLSLCrossCompilerContext* psContext, + const RESOURCE_DIMENSION eResDim, + Operand* psTexCoordOperand) +{ + uint32_t flags = TO_AUTO_BITCAST_TO_FLOAT; + bstring glsl = *psContext->currentShaderString; + uint32_t opMask = OPERAND_4_COMPONENT_MASK_ALL; + int isArray = 0; + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + //Vec1 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X; + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + //Vec2 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y; + flags |= TO_AUTO_EXPAND_TO_VEC2; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + case RESOURCE_DIMENSION_TEXTURE3D: + { + //Vec3 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z; + flags |= TO_AUTO_EXPAND_TO_VEC3; + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + //Vec3 texcoord. Mask out the other components. + opMask = OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y; + flags |= TO_AUTO_EXPAND_TO_VEC2; + isArray = 1; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + flags |= TO_AUTO_EXPAND_TO_VEC4; + break; + } + default: + { + ASSERT(0); + break; + } + } + + //FIXME detect when integer coords are needed. + TranslateOperandWithMaskMETAL(psContext, psTexCoordOperand, flags, opMask); + if (isArray) + { + bformata(glsl, ","); + TranslateOperandWithMaskMETAL(psContext, psTexCoordOperand, 0, OPERAND_4_COMPONENT_MASK_Z); + } +} + +static int METALGetNumTextureDimensions(HLSLCrossCompilerContext* psContext, + const RESOURCE_DIMENSION eResDim) +{ + (void)psContext; + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + return 1; + } + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + case RESOURCE_DIMENSION_TEXTURECUBE: + { + return 2; + } + + case RESOURCE_DIMENSION_TEXTURE3D: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + return 3; + } + default: + { + ASSERT(0); + break; + } + } + return 0; +} + +void GetResInfoDataMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, int index, int destElem) +{ + bstring metal = *psContext->currentShaderString; + int numParenthesis = 0; + const RESINFO_RETURN_TYPE eResInfoReturnType = psInst->eResInfoReturnType; + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + + AddIndentation(psContext); + METALAddOpAssignToDestWithMask(psContext, &psInst->asOperands[0], eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT ? SVT_UINT : SVT_FLOAT, 1, "=", &numParenthesis, 1 << destElem); + + //[width, height, depth or array size, total-mip-count] + if (index < 3) + { + int dim = METALGetNumTextureDimensions(psContext, eResDim); + bcatcstr(metal, "("); + if (dim < (index + 1)) + { + bcatcstr(metal, eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT ? "0u" : "0.0"); + } + else + { + if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) + { + bformata(metal, "uint%d(textureSize(", dim); + } + else if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_RCPFLOAT) + { + bformata(metal, "float%d(1.0) / float%d(textureSize(", dim, dim); + } + else + { + bformata(metal, "float%d(textureSize(", dim); + } + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ", "); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(metal, "))"); + + switch (index) + { + case 0: + bcatcstr(metal, ".x"); + break; + case 1: + bcatcstr(metal, ".y"); + break; + case 2: + bcatcstr(metal, ".z"); + break; + } + } + + bcatcstr(metal, ")"); + } + else + { + if (eResInfoReturnType == RESINFO_INSTRUCTION_RETURN_UINT) + { + bcatcstr(metal, "uint("); + } + else + { + bcatcstr(metal, "float("); + } + bcatcstr(metal, "textureQueryLevels("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, "))"); + } + METALAddAssignPrologue(psContext, numParenthesis); +} + +#define TEXSMP_FLAG_NONE 0x0 +#define TEXSMP_FLAG_LOD 0x1 //LOD comes from operand +#define TEXSMP_FLAG_DEPTHCOMPARE 0x2 +#define TEXSMP_FLAG_FIRSTLOD 0x4 //LOD is 0 +#define TEXSMP_FLAG_BIAS 0x8 +#define TEXSMP_FLAGS_GRAD 0x10 + +// TODO FIXME: non-float samplers! +static void METALTranslateTextureSample(HLSLCrossCompilerContext* psContext, Instruction* psInst, + uint32_t ui32Flags) +{ + bstring metal = *psContext->currentShaderString; + int numParenthesis = 0; + + const char* funcName = "sample"; + const char* depthCmpCoordType = ""; + const char* gradSwizzle = ""; + + uint32_t ui32NumOffsets = 0; + + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + + ASSERT(psInst->asOperands[2].ui32RegisterNumber < MAX_TEXTURES); + switch (eResDim) + { + case RESOURCE_DIMENSION_TEXTURE1D: + { + gradSwizzle = ".x"; + ui32NumOffsets = 1; + break; + } + case RESOURCE_DIMENSION_TEXTURE2D: + { + depthCmpCoordType = "float2"; + gradSwizzle = ".xy"; + ui32NumOffsets = 2; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBE: + { + depthCmpCoordType = "float3"; + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + break; + } + case RESOURCE_DIMENSION_TEXTURE3D: + { + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + break; + } + case RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + gradSwizzle = ".x"; + ui32NumOffsets = 1; + break; + } + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + depthCmpCoordType = "float2"; + gradSwizzle = ".xy"; + ui32NumOffsets = 2; + break; + } + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + //bformata(metal, "TODO:Sample from texture cube array LOD\n"); + gradSwizzle = ".xyz"; + ui32NumOffsets = 3; + //ASSERT(0); + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE) + { + //For non-cubeMap Arrays the reference value comes from the + //texture coord vector in GLSL. For cubmap arrays there is a + //separate parameter. + //It is always separate paramter in HLSL. + SHADER_VARIABLE_TYPE dataType = SVT_FLOAT; // TODO!! + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], dataType, GetNumSwizzleElementsMETAL(&psInst->asOperands[2]), &numParenthesis); + + bcatcstr(metal, "(float4("); + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, 0); + bformata(metal, ".%s_compare(", funcName); + bconcat(metal, TextureSamplerNameMETAL(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 1)); + bformata(metal, ", %s(", depthCmpCoordType); + METALTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + bcatcstr(metal, "), "); + //.z = reference. + TranslateOperandMETAL(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(metal, ", level(0)"); + } + + if (psInst->bAddressOffset) + { + if (ui32NumOffsets == 2) + { + bformata(metal, ", int2(%d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset); + } + else + if (ui32NumOffsets == 3) + { + bformata(metal, ", int3(%d, %d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset, + psInst->iWAddrOffset); + } + } + bcatcstr(metal, ")))"); + + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleWithMaskMETAL(psContext, &psInst->asOperands[2], GetOperandWriteMaskMETAL(&psInst->asOperands[0])); + } + else + { + SHADER_VARIABLE_TYPE dataType = SVT_FLOAT; // TODO!! + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], dataType, GetNumSwizzleElementsMETAL(&psInst->asOperands[2]), &numParenthesis); + + bcatcstr(metal, "("); + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, 0); + bformata(metal, ".%s(", funcName); + bconcat(metal, TextureSamplerNameMETAL(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, 0)); + bformata(metal, ", "); + METALTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + + if (ui32NumOffsets > 1) + { + if (ui32Flags & (TEXSMP_FLAG_LOD)) + { + bcatcstr(metal, ", level("); + TranslateOperandMETAL(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(metal, ")"); + } + else + if (ui32Flags & TEXSMP_FLAG_FIRSTLOD) + { + bcatcstr(metal, ", level(0)"); + } + else + if (ui32Flags & (TEXSMP_FLAG_BIAS)) + { + bcatcstr(metal, ", bias("); + TranslateOperandMETAL(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(metal, ")"); + } + else + if (ui32Flags & TEXSMP_FLAGS_GRAD) + { + if (eResDim == RESOURCE_DIMENSION_TEXTURECUBE) + { + bcatcstr(metal, ", gradientcube(float4("); + } + else + { + bformata(metal, ", gradient%dd(float4(", ui32NumOffsets); + } + + TranslateOperandMETAL(psContext, &psInst->asOperands[4], TO_AUTO_BITCAST_TO_FLOAT); //dx + bcatcstr(metal, ")"); + bcatcstr(metal, gradSwizzle); + bcatcstr(metal, ", float4("); + TranslateOperandMETAL(psContext, &psInst->asOperands[5], TO_AUTO_BITCAST_TO_FLOAT); //dy + bcatcstr(metal, ")"); + bcatcstr(metal, gradSwizzle); + bcatcstr(metal, ")"); + } + } + + if (psInst->bAddressOffset) + { + if (ui32NumOffsets == 1) + { + bformata(metal, ", %d", + psInst->iUAddrOffset); + } + else + if (ui32NumOffsets == 2) + { + bformata(metal, ", int2(%d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset); + } + else + if (ui32NumOffsets == 3) + { + bformata(metal, ", int3(%d, %d, %d)", + psInst->iUAddrOffset, + psInst->iVAddrOffset, + psInst->iWAddrOffset); + } + } + + bcatcstr(metal, "))"); + } + + if (!(ui32Flags & TEXSMP_FLAG_DEPTHCOMPARE)) + { + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleWithMaskMETAL(psContext, &psInst->asOperands[2], GetOperandWriteMaskMETAL(&psInst->asOperands[0])); + } + METALAddAssignPrologue(psContext, numParenthesis); +} + +static ShaderVarType* METALLookupStructuredVar(HLSLCrossCompilerContext* psContext, + Operand* psResource, + Operand* psByteOffset, + uint32_t ui32Component) +{ + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + uint32_t aui32Swizzle[4] = { OPERAND_4_COMPONENT_X }; + int byteOffset = ((int*)psByteOffset->afImmediates)[0] + 4 * ui32Component; + int vec4Offset = 0; + int32_t index = -1; + int32_t rebase = -1; + int found; + + ASSERT(psByteOffset->eType == OPERAND_TYPE_IMMEDIATE32); + //TODO: multi-component stores and vector writes need testing. + + //aui32Swizzle[0] = psInst->asOperands[0].aui32Swizzle[component]; + switch (psResource->eType) + { + case OPERAND_TYPE_RESOURCE: + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + GetConstantBufferFromBindingPoint(RGROUP_UAV, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + //dcl_tgsm_structured defines the amount of memory and a stride. + ASSERT(psResource->ui32RegisterNumber < MAX_GROUPSHARED); + return &psContext->psShader->sGroupSharedVarType[psResource->ui32RegisterNumber]; + } + default: + ASSERT(0); + break; + } + + switch (byteOffset % 16) + { + case 0: + aui32Swizzle[0] = 0; + break; + case 4: + aui32Swizzle[0] = 1; + break; + case 8: + aui32Swizzle[0] = 2; + break; + case 12: + aui32Swizzle[0] = 3; + break; + } + vec4Offset = byteOffset / 16; + + found = GetShaderVarFromOffset(vec4Offset, aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + ASSERT(found); + + return psVarType; +} + +static ShaderVarType* METALLookupStructuredVarAtomic(HLSLCrossCompilerContext* psContext, + Operand* psResource, + Operand* psByteOffset, + uint32_t ui32Component) +{ + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + uint32_t aui32Swizzle[4] = { OPERAND_4_COMPONENT_X }; + int byteOffset = ((int*)psByteOffset->afImmediates)[0] + 4 * ui32Component; + int vec4Offset = 0; + int32_t index = -1; + int32_t rebase = -1; + int found; + + ASSERT(psByteOffset->eType == OPERAND_TYPE_IMMEDIATE32); + //TODO: multi-component stores and vector writes need testing. + + //aui32Swizzle[0] = psInst->asOperands[0].aui32Swizzle[component]; + switch (psResource->eType) + { + case OPERAND_TYPE_RESOURCE: + GetConstantBufferFromBindingPoint(RGROUP_TEXTURE, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + GetConstantBufferFromBindingPoint(RGROUP_UAV, psResource->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + break; + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + //dcl_tgsm_structured defines the amount of memory and a stride. + ASSERT(psResource->ui32RegisterNumber < MAX_GROUPSHARED); + return &psContext->psShader->sGroupSharedVarType[psResource->ui32RegisterNumber]; + } + default: + ASSERT(0); + break; + } + + if (psCBuf->asVars->sType.Class == SVC_STRUCT) + { + //recalculate offset based on address.y; + int offset = *((int*)(&psByteOffset->afImmediates[1])); + if (offset > 0) + { + byteOffset = offset + 4 * ui32Component; + } + } + + switch (byteOffset % 16) + { + case 0: + aui32Swizzle[0] = 0; + break; + case 4: + aui32Swizzle[0] = 1; + break; + case 8: + aui32Swizzle[0] = 2; + break; + case 12: + aui32Swizzle[0] = 3; + break; + } + vec4Offset = byteOffset / 16; + + found = GetShaderVarFromOffset(vec4Offset, aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + ASSERT(found); + + return psVarType; +} + +static void METALTranslateShaderStorageStore(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring metal = *psContext->currentShaderString; + ShaderVarType* psVarType = NULL; + int component; + int srcComponent = 0; + + Operand* psDest = 0; + Operand* psDestAddr = 0; + Operand* psDestByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_STORE_STRUCTURED: + psDest = &psInst->asOperands[0]; + psDestAddr = &psInst->asOperands[1]; + psDestByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + + break; + case OPCODE_STORE_RAW: + psDest = &psInst->asOperands[0]; + psDestByteOff = &psInst->asOperands[1]; + psSrc = &psInst->asOperands[2]; + break; + } + + for (component = 0; component < 4; component++) + { + const char* swizzleString[] = { ".x", ".y", ".z", ".w" }; + ASSERT(psInst->asOperands[0].eSelMode == OPERAND_4_COMPONENT_MASK_MODE); + if (psInst->asOperands[0].ui32CompMask & (1 << component)) + { + + if (structured && psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + psVarType = METALLookupStructuredVar(psContext, psDest, psDestByteOff, component); + } + + AddIndentation(psContext); + + if (!structured && (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY)) + { + bformata(metal, "atomic_store_explicit( &"); + TranslateOperandMETAL(psContext, psDest, TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY); + bformata(metal, "["); + if (structured) //Dest address and dest byte offset + { + if (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + TranslateOperandMETAL(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, "].value["); + TranslateOperandMETAL(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, "/4u ");//bytes to floats + } + else + { + TranslateOperandMETAL(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + } + else + { + TranslateOperandMETAL(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + //RAW: change component using index offset + if (!structured || (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY)) + { + bformata(metal, " + %d", component); + } + bformata(metal, "],"); + + if (structured) + { + uint32_t flags = TO_FLAG_UNSIGNED_INTEGER; + if (psVarType) + { + if (psVarType->Type == SVT_INT) + { + flags = TO_FLAG_INTEGER; + } + else if (psVarType->Type == SVT_FLOAT) + { + flags = TO_FLAG_NONE; + } + else if (psVarType->Type == SVT_FLOAT16) + { + flags = TO_FLAG_FLOAT16; + } + else + { + ASSERT(0); + } + } + //TGSM always uint + bformata(metal, " ("); + if (GetNumSwizzleElementsMETAL(psSrc) > 1) + { + TranslateOperandWithMaskMETAL(psContext, psSrc, flags, 1 << (srcComponent++)); + } + else + { + TranslateOperandWithMaskMETAL(psContext, psSrc, flags, OPERAND_4_COMPONENT_MASK_X); + } + } + else + { + //Dest type is currently always a uint array. + bformata(metal, " ("); + if (GetNumSwizzleElementsMETAL(psSrc) > 1) + { + TranslateOperandWithMaskMETAL(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER, 1 << (srcComponent++)); + } + else + { + TranslateOperandWithMaskMETAL(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER, OPERAND_4_COMPONENT_MASK_X); + } + } + + //Double takes an extra slot. + if (psVarType && psVarType->Type == SVT_DOUBLE) + { + if (structured && psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + bcatcstr(metal, ")"); + } + component++; + } + + bformata(metal, "),"); + bformata(metal, "memory_order_relaxed"); + bformata(metal, ");\n"); + return; + } + + if (structured && psDest->eType == OPERAND_TYPE_RESOURCE) + { + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psDest->ui32RegisterNumber, 0); + } + else + { + TranslateOperandMETAL(psContext, psDest, TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY); + } + bformata(metal, "["); + if (structured) //Dest address and dest byte offset + { + if (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + TranslateOperandMETAL(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, "].value["); + TranslateOperandMETAL(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, "/4u ");//bytes to floats + } + else + { + TranslateOperandMETAL(psContext, psDestAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + } + else + { + TranslateOperandMETAL(psContext, psDestByteOff, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + } + + //RAW: change component using index offset + if (!structured || (psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY)) + { + bformata(metal, " + %d", component); + } + + bformata(metal, "]"); + + if (structured && psDest->eType != OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + if (strcmp(psVarType->Name, "$Element") != 0) + { + bformata(metal, ".%s", psVarType->Name); + } + if (psVarType->Columns > 1 || psVarType->Rows > 1) + { + bformata(metal, "%s", swizzleString[((((int*)psDestByteOff->afImmediates)[0] + 4 * component - psVarType->Offset) % 16 / 4)]); + } + } + + if (structured) + { + uint32_t flags = TO_FLAG_UNSIGNED_INTEGER; + if (psVarType) + { + if (psVarType->Type == SVT_INT) + { + flags = TO_FLAG_INTEGER; + } + else if (psVarType->Type == SVT_FLOAT) + { + flags = TO_FLAG_NONE; + } + else if (psVarType->Type == SVT_FLOAT16) + { + flags = TO_FLAG_FLOAT16; + } + else + { + ASSERT(0); + } + } + //TGSM always uint + bformata(metal, " = ("); + if (GetNumSwizzleElementsMETAL(psSrc) > 1) + { + TranslateOperandWithMaskMETAL(psContext, psSrc, flags, 1 << (srcComponent++)); + } + else + { + TranslateOperandWithMaskMETAL(psContext, psSrc, flags, OPERAND_4_COMPONENT_MASK_X); + } + } + else + { + //Dest type is currently always a uint array. + bformata(metal, " = ("); + if (GetNumSwizzleElementsMETAL(psSrc) > 1) + { + TranslateOperandWithMaskMETAL(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER, 1 << (srcComponent++)); + } + else + { + TranslateOperandWithMaskMETAL(psContext, psSrc, TO_FLAG_UNSIGNED_INTEGER, OPERAND_4_COMPONENT_MASK_X); + } + } + + //Double takes an extra slot. + if (psVarType && psVarType->Type == SVT_DOUBLE) + { + if (structured && psDest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + bcatcstr(metal, ")"); + } + component++; + } + + bformata(metal, ");\n"); + } + } +} + +static void METALTranslateShaderStorageLoad(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring metal = *psContext->currentShaderString; + int component; + Operand* psDest = 0; + Operand* psSrcAddr = 0; + Operand* psSrcByteOff = 0; + Operand* psSrc = 0; + int structured = 0; + + switch (psInst->eOpcode) + { + case OPCODE_LD_STRUCTURED: + psDest = &psInst->asOperands[0]; + psSrcAddr = &psInst->asOperands[1]; + psSrcByteOff = &psInst->asOperands[2]; + psSrc = &psInst->asOperands[3]; + structured = 1; + break; + case OPCODE_LD_RAW: + psDest = &psInst->asOperands[0]; + psSrcByteOff = &psInst->asOperands[1]; + psSrc = &psInst->asOperands[2]; + break; + } + + if (psInst->eOpcode == OPCODE_LD_RAW) + { + int numParenthesis = 0; + int firstItemAdded = 0; + uint32_t destCount = GetNumSwizzleElementsMETAL(psDest); + uint32_t destMask = GetOperandWriteMaskMETAL(psDest); + AddIndentation(psContext); + METALAddAssignToDest(psContext, psDest, SVT_UINT, destCount, &numParenthesis); + if (destCount > 1) + { + bformata(metal, "%s(", GetConstructorForTypeMETAL(SVT_UINT, destCount)); + numParenthesis++; + } + for (component = 0; component < 4; component++) + { + if (!(destMask & (1 << component))) + { + continue; + } + + if (firstItemAdded) + { + bcatcstr(metal, ", "); + } + else + { + firstItemAdded = 1; + } + + if (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + //ld from threadgroup shared memory + bformata(metal, "atomic_load_explicit( &"); + bformata(metal, "TGSM%d[((", psSrc->ui32RegisterNumber); + TranslateOperandMETAL(psContext, psSrcByteOff, TO_FLAG_INTEGER); + bcatcstr(metal, ") >> 2)"); + if (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE && psSrc->aui32Swizzle[component] != 0) + { + bformata(metal, " + %d", psSrc->aui32Swizzle[component]); + } + bcatcstr(metal, "]"); + bcatcstr(metal, " , "); + bcatcstr(metal, "memory_order::memory_order_relaxed"); + bformata(metal, ")"); + + /* + bformata(metal, "TGSM%d[((", psSrc->ui32RegisterNumber); + TranslateOperandMETAL(psContext, psSrcByteOff, TO_FLAG_INTEGER); + bcatcstr(metal, ") >> 2)"); + if (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE && psSrc->aui32Swizzle[component] != 0) + { + bformata(metal, " + %d", psSrc->aui32Swizzle[component]); + } + bcatcstr(metal, "]"); + */ + } + else + { + //ld from raw buffer + bformata(metal, "RawRes%d[((", psSrc->ui32RegisterNumber); + TranslateOperandMETAL(psContext, psSrcByteOff, TO_FLAG_INTEGER); + bcatcstr(metal, ") >> 2)"); + if (psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE && psSrc->aui32Swizzle[component] != 0) + { + bformata(metal, " + %d", psSrc->aui32Swizzle[component]); + } + bcatcstr(metal, "]"); + } + } + METALAddAssignPrologue(psContext, numParenthesis); + } + else + { + int numParenthesis = 0; + int firstItemAdded = 0; + uint32_t destCount = GetNumSwizzleElementsMETAL(psDest); + uint32_t destMask = GetOperandWriteMaskMETAL(psDest); + ASSERT(psInst->eOpcode == OPCODE_LD_STRUCTURED); + AddIndentation(psContext); + METALAddAssignToDest(psContext, psDest, SVT_UINT, destCount, &numParenthesis); + if (destCount > 1) + { + bformata(metal, "%s(", GetConstructorForTypeMETAL(SVT_UINT, destCount)); + numParenthesis++; + } + for (component = 0; component < 4; component++) + { + ShaderVarType* psVar = NULL; + int addedBitcast = 0; + if (!(destMask & (1 << component))) + { + continue; + } + + if (firstItemAdded) + { + bcatcstr(metal, ", "); + } + else + { + firstItemAdded = 1; + } + + if (psSrc->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + // input already in uints + TranslateOperandMETAL(psContext, psSrc, TO_FLAG_NAME_ONLY); + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(metal, "].value[("); + TranslateOperandMETAL(psContext, psSrcByteOff, TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, " >> 2u) + %d]", psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + } + else + { + ConstantBuffer* psCBuf = NULL; + psVar = METALLookupStructuredVar(psContext, psSrc, psSrcByteOff, psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component); + GetConstantBufferFromBindingPoint(RGROUP_UAV, psSrc->ui32RegisterNumber, &psContext->psShader->sInfo, &psCBuf); + + if (psVar->Type == SVT_FLOAT) + { + bcatcstr(metal, "as_type("); + bcatcstr(metal, "("); + addedBitcast = 1; + } + else if (psVar->Type == SVT_DOUBLE) + { + bcatcstr(metal, "as_type("); + bcatcstr(metal, "("); + addedBitcast = 1; + } + if (psSrc->eType == OPERAND_TYPE_UNORDERED_ACCESS_VIEW) + { + bformata(metal, "%s[", psCBuf->Name); + TranslateOperandMETAL(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(metal, "]"); + if (strcmp(psVar->Name, "$Element") != 0) + { + bcatcstr(metal, "."); + bcatcstr(metal, psVar->Name); + } + + int swizcomponent = psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component; + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * swizcomponent; + int bytes = byteOffset - psVar->Offset; + if (psVar->Class != SVC_SCALAR) + { + static const char* const m_swizzlers[] = { "x", "y", "z", "w" }; + int offset = (bytes % 16) / 4; + if (offset == 0) + { + bcatcstr(metal, ".x"); + } + if (offset == 1) + { + bcatcstr(metal, ".y"); + } + if (offset == 2) + { + bcatcstr(metal, ".z"); + } + if (offset == 3) + { + bcatcstr(metal, ".w"); + } + } + } + else + { + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psSrc->ui32RegisterNumber, 0); + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psSrcAddr, TO_FLAG_INTEGER); + bcatcstr(metal, "]"); + if (strcmp(psVar->Name, "$Element") != 0) + { + bcatcstr(metal, "."); + bcatcstr(metal, psVar->Name); + int swizcomponent = psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component; + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * swizcomponent; + int bytes = byteOffset - psVar->Offset; + if (psVar->Class == SVC_MATRIX_ROWS) + { + int offset = bytes / 16; + bcatcstr(metal, "["); + bformata(metal, "%i", offset); + bcatcstr(metal, "]"); + } + if (psVar->Class != SVC_SCALAR) + { + static const char* const m_swizzlers[] = { "x", "y", "z", "w" }; + + int offset = (bytes % 16) / 4; + if (offset == 0) + { + bcatcstr(metal, ".x"); + } + if (offset == 1) + { + bcatcstr(metal, ".y"); + } + if (offset == 2) + { + bcatcstr(metal, ".z"); + } + if (offset == 3) + { + bcatcstr(metal, ".w"); + } + } + } + else if (psVar->Columns > 1) + { + int swizcomponent = psSrc->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE ? psSrc->aui32Swizzle[component] : component; + int byteOffset = ((int*)psSrcByteOff->afImmediates)[0] + 4 * swizcomponent; + int bytes = byteOffset - psVar->Offset; + + static const char* const m_swizzlers[] = { "x", "y", "z", "w" }; + + int offset = (bytes % 16) / 4; + if (offset == 0) + { + bcatcstr(metal, ".x"); + } + if (offset == 1) + { + bcatcstr(metal, ".y"); + } + if (offset == 2) + { + bcatcstr(metal, ".z"); + } + if (offset == 3) + { + bcatcstr(metal, ".w"); + } + } + } + + if (addedBitcast) + { + bcatcstr(metal, "))"); + } + + if (psVar->Columns > 1) + { + int multiplier = 1; + + if (psVar->Type == SVT_DOUBLE) + { + multiplier++; // doubles take up 2 slots + } + //component += psVar->Columns * multiplier; + } + } + } + METALAddAssignPrologue(psContext, numParenthesis); + + return; + } +} + +void TranslateAtomicMemOpMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst) +{ + bstring metal = *psContext->currentShaderString; + int numParenthesis = 0; + ShaderVarType* psVarType = NULL; + uint32_t ui32DataTypeFlag = TO_FLAG_UNSIGNED_INTEGER; + const char* func = ""; + Operand* dest = 0; + Operand* previousValue = 0; + Operand* destAddr = 0; + Operand* src = 0; + Operand* compare = 0; + + switch (psInst->eOpcode) + { + case OPCODE_IMM_ATOMIC_IADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_IADD\n"); +#endif + func = "atomic_fetch_add_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_IADD\n"); +#endif + func = "atomic_fetch_add_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_AND\n"); +#endif + func = "atomic_fetch_and_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_AND\n"); +#endif + func = "atomic_fetch_and_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_OR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_OR\n"); +#endif + func = "atomic_fetch_or_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_OR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_OR\n"); +#endif + func = "atomic_fetch_or_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_XOR\n"); +#endif + func = "atomic_fetch_xor_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_XOR\n"); +#endif + func = "atomic_fetch_xor_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + + case OPCODE_IMM_ATOMIC_EXCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_EXCH\n"); +#endif + func = "atomic_exchange_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_CMP_EXC\n"); +#endif + func = "atomic_compare_exchange_weak_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + compare = &psInst->asOperands[3]; + src = &psInst->asOperands[4]; + break; + } + case OPCODE_ATOMIC_CMP_STORE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_CMP_STORE\n"); +#endif + func = "atomic_compare_exchange_weak_explicit"; + previousValue = 0; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + compare = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_IMM_ATOMIC_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_UMIN\n"); +#endif + func = "atomic_fetch_min_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_UMIN\n"); +#endif + func = "atomic_fetch_min_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_IMIN\n"); +#endif + func = "atomic_fetch_min_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_IMIN\n"); +#endif + func = "atomic_fetch_min_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_UMAX\n"); +#endif + func = "atomic_fetch_max_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_UMAX\n"); +#endif + func = "atomic_fetch_max_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + case OPCODE_IMM_ATOMIC_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMM_ATOMIC_IMAX\n"); +#endif + func = "atomic_fetch_max_explicit"; + previousValue = &psInst->asOperands[0]; + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + src = &psInst->asOperands[3]; + break; + } + case OPCODE_ATOMIC_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ATOMIC_IMAX\n"); +#endif + func = "atomic_fetch_max_explicit"; + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + src = &psInst->asOperands[2]; + break; + } + } + + AddIndentation(psContext); + + if (previousValue) + { + //all atomic operation returns uint or int + METALAddAssignToDest(psContext, previousValue, SVT_UINT, 1, &numParenthesis); + } + + bcatcstr(metal, func); + bformata(metal, "( &"); + TranslateOperandMETAL(psContext, dest, TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY); + + if (dest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + //threadgroup shared mem + bformata(metal, "["); + TranslateOperandMETAL(psContext, destAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, "]"); + } + else + { + ResourceBinding* psRes; +#if defined(_DEBUG) + int foundResource = +#endif + GetResourceFromBindingPoint(RGROUP_UAV, + dest->ui32RegisterNumber, + &psContext->psShader->sInfo, + &psRes); + + ASSERT(foundResource); + + if (psRes->eBindArea == UAVAREA_CBUFFER) + { + //rwbuffer + if (psRes->eType == RTYPE_UAV_RWTYPED) + { + bformata(metal, "["); + TranslateOperandMETAL(psContext, destAddr, TO_FLAG_INTEGER | TO_FLAG_UNSIGNED_INTEGER); + bformata(metal, "]"); + } + //rwstructured buffer + else if (psRes->eType == RTYPE_UAV_RWSTRUCTURED) + { + if (destAddr->eType == OPERAND_TYPE_IMMEDIATE32) + { + psVarType = METALLookupStructuredVarAtomic(psContext, dest, destAddr, 0); + } + if (psVarType->Type == SVT_UINT) + { + ui32DataTypeFlag = TO_FLAG_UNSIGNED_INTEGER | TO_AUTO_BITCAST_TO_UINT; + } + else + { + ui32DataTypeFlag = TO_FLAG_INTEGER | TO_AUTO_BITCAST_TO_INT; + } + bformata(metal, "["); + bformata(metal, "%i", *((int*)(&destAddr->afImmediates[0]))); + bformata(metal, "]"); + if (strcmp(psVarType->Name, "$Element") != 0) + { + bformata(metal, ".%s", psVarType->Name); + } + } + } + else if (psRes->eBindArea == UAVAREA_TEXTURE) + { + //Atomic operation on texture uav not supported + ASSERT(0); + } + else + { + //UAV is not exist in either [[buffer]] or [[texture]] + ASSERT(0); + } + } + //ResourceNameMETAL(metal, psContext, RGROUP_UAV, dest->ui32RegisterNumber, 0); + + bcatcstr(metal, ", "); + + if (compare) + { + bcatcstr(metal, "& "); + TranslateOperandMETAL(psContext, compare, ui32DataTypeFlag); + bcatcstr(metal, ", "); + } + + TranslateOperandMETAL(psContext, src, ui32DataTypeFlag); + bcatcstr(metal, ", "); + if (compare) + { + bcatcstr(metal, "memory_order_relaxed "); + bcatcstr(metal, ","); + } + bcatcstr(metal, "memory_order_relaxed "); + bcatcstr(metal, ")"); + if (previousValue) + { + METALAddAssignPrologue(psContext, numParenthesis); + } + else + { + bcatcstr(metal, ";\n"); + } +} + +static void METALTranslateConditional(HLSLCrossCompilerContext* psContext, + Instruction* psInst, + bstring glsl) +{ + const char* statement = ""; + if (psInst->eOpcode == OPCODE_BREAKC) + { + statement = "break"; + } + else if (psInst->eOpcode == OPCODE_CONTINUEC) + { + statement = "continue"; + } + else if (psInst->eOpcode == OPCODE_RETC) + { + statement = "return"; + } + + if (psInst->eBooleanTestType == INSTRUCTION_TEST_ZERO) + { + bcatcstr(glsl, "if(("); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER); + + if (psInst->eOpcode != OPCODE_IF) + { + bformata(glsl, ")==0u){%s;}\n", statement); + } + else + { + bcatcstr(glsl, ")==0u){\n"); + } + } + else + { + ASSERT(psInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO); + bcatcstr(glsl, "if(("); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER); + + if (psInst->eOpcode != OPCODE_IF) + { + bformata(glsl, ")!=0u){%s;}\n", statement); + } + else + { + bcatcstr(glsl, ")!=0u){\n"); + } + } +} + +// Returns the "more important" type of a and b, currently int < uint < float +static SHADER_VARIABLE_TYPE METALSelectHigherType(SHADER_VARIABLE_TYPE a, SHADER_VARIABLE_TYPE b) +{ + if (a == SVT_FLOAT || b == SVT_FLOAT) + { + return SVT_FLOAT; + } + + if (a == SVT_FLOAT16 || b == SVT_FLOAT16) + { + return SVT_FLOAT16; + } + // Apart from floats, the enum values are fairly well-ordered, use that directly. + return a > b ? a : b; +} + +// Helper function to set the vector type of 1 or more components in a vector +// If the existing values (that we're writing to) are all SVT_VOID, just upgrade the value and we're done +// Otherwise, set all the components in the vector that currently are set to that same value OR are now being written to +// to the "highest" type value (ordering int->uint->float) +static void METALSetVectorType(SHADER_VARIABLE_TYPE* aeTempVecType, uint32_t regBaseIndex, uint32_t componentMask, SHADER_VARIABLE_TYPE eType) +{ + int existingTypesFound = 0; + int i = 0; + for (i = 0; i < 4; i++) + { + if (componentMask & (1 << i)) + { + if (aeTempVecType[regBaseIndex + i] != SVT_VOID) + { + existingTypesFound = 1; + break; + } + } + } + + if (existingTypesFound != 0) + { + // Expand the mask to include all components that are used, also upgrade type + for (i = 0; i < 4; i++) + { + if (aeTempVecType[regBaseIndex + i] != SVT_VOID) + { + componentMask |= (1 << i); + eType = METALSelectHigherType(eType, aeTempVecType[regBaseIndex + i]); + } + } + } + + // Now componentMask contains the components we actually need to update and eType may have been changed to something else. + // Write the results + for (i = 0; i < 4; i++) + { + if (componentMask & (1 << i)) + { + aeTempVecType[regBaseIndex + i] = eType; + } + } +} + +static void METALMarkOperandAs(Operand* psOperand, SHADER_VARIABLE_TYPE eType, SHADER_VARIABLE_TYPE* aeTempVecType) +{ + if (psOperand->eType == OPERAND_TYPE_INDEXABLE_TEMP || psOperand->eType == OPERAND_TYPE_TEMP) + { + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + METALSetVectorType(aeTempVecType, ui32RegIndex, 1 << psOperand->aui32Swizzle[0], eType); + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + // 0xf == all components, swizzle order doesn't matter. + METALSetVectorType(aeTempVecType, ui32RegIndex, 0xf, eType); + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + METALSetVectorType(aeTempVecType, ui32RegIndex, ui32CompMask, eType); + } + } +} + +static void METALMarkAllOperandsAs(Instruction* psInst, SHADER_VARIABLE_TYPE eType, SHADER_VARIABLE_TYPE* aeTempVecType) +{ + uint32_t i = 0; + for (i = 0; i < psInst->ui32NumOperands; i++) + { + METALMarkOperandAs(&psInst->asOperands[i], eType, aeTempVecType); + } +} + +static void METALWriteOperandTypes(Operand* psOperand, const SHADER_VARIABLE_TYPE* aeTempVecType) +{ + const uint32_t ui32RegIndex = psOperand->ui32RegisterNumber * 4; + + if (psOperand->eType != OPERAND_TYPE_TEMP) + { + return; + } + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + psOperand->aeDataType[0] = aeTempVecType[ui32RegIndex]; + psOperand->aeDataType[1] = aeTempVecType[ui32RegIndex + 1]; + psOperand->aeDataType[2] = aeTempVecType[ui32RegIndex + 2]; + psOperand->aeDataType[3] = aeTempVecType[ui32RegIndex + 3]; + } + else + { + psOperand->aeDataType[psOperand->aui32Swizzle[0]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[0]]; + psOperand->aeDataType[psOperand->aui32Swizzle[1]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[1]]; + psOperand->aeDataType[psOperand->aui32Swizzle[2]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[2]]; + psOperand->aeDataType[psOperand->aui32Swizzle[3]] = aeTempVecType[ui32RegIndex + psOperand->aui32Swizzle[3]]; + } + } + else if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + int c = 0; + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + + for (; c < 4; ++c) + { + if (ui32CompMask & (1 << c)) + { + psOperand->aeDataType[c] = aeTempVecType[ui32RegIndex + c]; + } + } + } +} + +// Mark scalars from CBs. TODO: Do we need to do the same for vec2/3's as well? There may be swizzles involved which make it vec4 or something else again. +static void METALSetCBOperandComponents(HLSLCrossCompilerContext* psContext, Operand* psOperand) +{ + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + int rebase = 0; + + if (psOperand->eType != OPERAND_TYPE_CONSTANT_BUFFER) + { + return; + } + + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + + if (psVarType->Class == SVC_SCALAR) + { + psOperand->iNumComponents = 1; + } +} + + +void SetDataTypesMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, const int32_t i32InstCount) +{ + int32_t i; + Instruction* psFirstInst = psInst; + + SHADER_VARIABLE_TYPE aeTempVecType[MAX_TEMP_VEC4 * 4]; + + // Start with void, then move up the chain void->int->uint->float + for (i = 0; i < MAX_TEMP_VEC4 * 4; ++i) + { + aeTempVecType[i] = SVT_VOID; + } + + { + // First pass, do analysis: deduce the data type based on opcodes, fill out aeTempVecType table + // Only ever to int->float promotion (or int->uint), never the other way around + for (i = 0; i < i32InstCount; ++i, psInst++) + { + if (psInst->ui32NumOperands == 0) + { + continue; + } + + switch (psInst->eOpcode) + { + // All float-only ops + case OPCODE_ADD: + case OPCODE_DERIV_RTX: + case OPCODE_DERIV_RTY: + case OPCODE_DIV: + case OPCODE_DP2: + case OPCODE_DP3: + case OPCODE_DP4: + case OPCODE_EQ: + case OPCODE_EXP: + case OPCODE_FRC: + case OPCODE_LOG: + case OPCODE_MAD: + case OPCODE_MIN: + case OPCODE_MAX: + case OPCODE_MUL: + case OPCODE_NE: + case OPCODE_ROUND_NE: + case OPCODE_ROUND_NI: + case OPCODE_ROUND_PI: + case OPCODE_ROUND_Z: + case OPCODE_RSQ: + case OPCODE_SAMPLE: + case OPCODE_SAMPLE_C: + case OPCODE_SAMPLE_C_LZ: + case OPCODE_SAMPLE_L: + case OPCODE_SAMPLE_D: + case OPCODE_SAMPLE_B: + case OPCODE_SQRT: + case OPCODE_SINCOS: + case OPCODE_LOD: + case OPCODE_GATHER4: + + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_GATHER4_C: + case OPCODE_GATHER4_PO: + case OPCODE_GATHER4_PO_C: + case OPCODE_RCP: + + METALMarkAllOperandsAs(psInst, SVT_FLOAT, aeTempVecType); + break; + + // Int-only ops, no need to do anything + case OPCODE_AND: + case OPCODE_BREAKC: + case OPCODE_CALLC: + case OPCODE_CONTINUEC: + case OPCODE_IADD: + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INE: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_IF: + case OPCODE_NOT: + case OPCODE_OR: + case OPCODE_RETC: + case OPCODE_XOR: + case OPCODE_BUFINFO: + case OPCODE_COUNTBITS: + case OPCODE_FIRSTBIT_HI: + case OPCODE_FIRSTBIT_LO: + case OPCODE_FIRSTBIT_SHI: + case OPCODE_UBFE: + case OPCODE_IBFE: + case OPCODE_BFI: + case OPCODE_BFREV: + case OPCODE_ATOMIC_AND: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_IMAX: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_MOV: + case OPCODE_MOVC: + case OPCODE_SWAPC: + METALMarkAllOperandsAs(psInst, SVT_INT, aeTempVecType); + break; + // uint ops + case OPCODE_UDIV: + case OPCODE_ULT: + case OPCODE_UGE: + case OPCODE_UMUL: + case OPCODE_UMAD: + case OPCODE_UMAX: + case OPCODE_UMIN: + case OPCODE_USHR: + case OPCODE_UADDC: + case OPCODE_USUBB: + METALMarkAllOperandsAs(psInst, SVT_UINT, aeTempVecType); + break; + + // Need special handling + case OPCODE_FTOI: + case OPCODE_FTOU: + METALMarkOperandAs(&psInst->asOperands[0], psInst->eOpcode == OPCODE_FTOI ? SVT_INT : SVT_UINT, aeTempVecType); + METALMarkOperandAs(&psInst->asOperands[1], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_GE: + case OPCODE_LT: + METALMarkOperandAs(&psInst->asOperands[0], SVT_UINT, aeTempVecType); + METALMarkOperandAs(&psInst->asOperands[1], SVT_FLOAT, aeTempVecType); + METALMarkOperandAs(&psInst->asOperands[2], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_ITOF: + case OPCODE_UTOF: + METALMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + METALMarkOperandAs(&psInst->asOperands[1], psInst->eOpcode == OPCODE_ITOF ? SVT_INT : SVT_UINT, aeTempVecType); + break; + + case OPCODE_LD: + case OPCODE_LD_MS: + // TODO: Would need to know the sampler return type + METALMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + break; + + + case OPCODE_RESINFO: + { + if (psInst->eResInfoReturnType != RESINFO_INSTRUCTION_RETURN_UINT) + { + METALMarkAllOperandsAs(psInst, SVT_FLOAT, aeTempVecType); + } + break; + } + + case OPCODE_SAMPLE_INFO: + // TODO decode the _uint flag + METALMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + break; + + case OPCODE_SAMPLE_POS: + METALMarkOperandAs(&psInst->asOperands[0], SVT_FLOAT, aeTempVecType); + break; + + + case OPCODE_LD_UAV_TYPED: + case OPCODE_STORE_UAV_TYPED: + case OPCODE_LD_RAW: + case OPCODE_STORE_RAW: + case OPCODE_LD_STRUCTURED: + case OPCODE_STORE_STRUCTURED: + { + METALMarkOperandAs(&psInst->asOperands[0], SVT_INT, aeTempVecType); + break; + } + case OPCODE_F32TOF16: + case OPCODE_F16TOF32: + // TODO + break; + + + + // No-operands, should never get here anyway + /* case OPCODE_BREAK: + case OPCODE_CALL: + case OPCODE_CASE: + case OPCODE_CONTINUE: + case OPCODE_CUT: + case OPCODE_DEFAULT: + case OPCODE_DISCARD: + case OPCODE_ELSE: + case OPCODE_EMIT: + case OPCODE_EMITTHENCUT: + case OPCODE_ENDIF: + case OPCODE_ENDLOOP: + case OPCODE_ENDSWITCH: + + case OPCODE_LABEL: + case OPCODE_LOOP: + case OPCODE_CUSTOMDATA: + case OPCODE_NOP: + case OPCODE_RET: + case OPCODE_SWITCH: + case OPCODE_DCL_RESOURCE: // DCL* opcodes have + case OPCODE_DCL_CONSTANT_BUFFER: // custom operand formats. + case OPCODE_DCL_SAMPLER: + case OPCODE_DCL_INDEX_RANGE: + case OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY: + case OPCODE_DCL_GS_INPUT_PRIMITIVE: + case OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT: + case OPCODE_DCL_INPUT: + case OPCODE_DCL_INPUT_SGV: + case OPCODE_DCL_INPUT_SIV: + case OPCODE_DCL_INPUT_PS: + case OPCODE_DCL_INPUT_PS_SGV: + case OPCODE_DCL_INPUT_PS_SIV: + case OPCODE_DCL_OUTPUT: + case OPCODE_DCL_OUTPUT_SGV: + case OPCODE_DCL_OUTPUT_SIV: + case OPCODE_DCL_TEMPS: + case OPCODE_DCL_INDEXABLE_TEMP: + case OPCODE_DCL_GLOBAL_FLAGS: + + + case OPCODE_HS_DECLS: // token marks beginning of HS sub-shader + case OPCODE_HS_CONTROL_POINT_PHASE: // token marks beginning of HS sub-shader + case OPCODE_HS_FORK_PHASE: // token marks beginning of HS sub-shader + case OPCODE_HS_JOIN_PHASE: // token marks beginning of HS sub-shader + + case OPCODE_EMIT_STREAM: + case OPCODE_CUT_STREAM: + case OPCODE_EMITTHENCUT_STREAM: + case OPCODE_INTERFACE_CALL: + + + case OPCODE_DCL_STREAM: + case OPCODE_DCL_FUNCTION_BODY: + case OPCODE_DCL_FUNCTION_TABLE: + case OPCODE_DCL_INTERFACE: + + case OPCODE_DCL_INPUT_CONTROL_POINT_COUNT: + case OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT: + case OPCODE_DCL_TESS_DOMAIN: + case OPCODE_DCL_TESS_PARTITIONING: + case OPCODE_DCL_TESS_OUTPUT_PRIMITIVE: + case OPCODE_DCL_HS_MAX_TESSFACTOR: + case OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT: + case OPCODE_DCL_HS_JOIN_PHASE_INSTANCE_COUNT: + + case OPCODE_DCL_THREAD_GROUP: + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED: + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW: + case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED: + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW: + case OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED: + case OPCODE_DCL_RESOURCE_RAW: + case OPCODE_DCL_RESOURCE_STRUCTURED: + case OPCODE_SYNC: + + // TODO + case OPCODE_DADD: + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DMOV: + case OPCODE_DMOVC: + case OPCODE_DTOF: + case OPCODE_FTOD: + + case OPCODE_EVAL_SNAPPED: + case OPCODE_EVAL_SAMPLE_INDEX: + case OPCODE_EVAL_CENTROID: + + case OPCODE_DCL_GS_INSTANCE_COUNT: + + case OPCODE_ABORT: + case OPCODE_DEBUG_BREAK:*/ + + default: + break; + } + } + } + + // Fill the rest of aeTempVecType, just in case. + for (i = 0; i < MAX_TEMP_VEC4 * 4; i++) + { + if (aeTempVecType[i] == SVT_VOID) + { + aeTempVecType[i] = SVT_INT; + } + } + + // Now the aeTempVecType table has been filled with (mostly) valid data, write it back to all operands + psInst = psFirstInst; + for (i = 0; i < i32InstCount; ++i, psInst++) + { + int k = 0; + + if (psInst->ui32NumOperands == 0) + { + continue; + } + + //Preserve the current type on dest array index + if (psInst->asOperands[0].eType == OPERAND_TYPE_INDEXABLE_TEMP) + { + Operand* psSubOperand = psInst->asOperands[0].psSubOperand[1]; + if (psSubOperand != 0) + { + METALWriteOperandTypes(psSubOperand, aeTempVecType); + } + } + if (psInst->asOperands[0].eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + METALSetCBOperandComponents(psContext, &psInst->asOperands[0]); + } + + //Preserve the current type on sources. + for (k = psInst->ui32NumOperands - 1; k >= (int)psInst->ui32FirstSrc; --k) + { + int32_t subOperand; + Operand* psOperand = &psInst->asOperands[k]; + + METALWriteOperandTypes(psOperand, aeTempVecType); + if (psOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + METALSetCBOperandComponents(psContext, psOperand); + } + + for (subOperand = 0; subOperand < MAX_SUB_OPERANDS; subOperand++) + { + if (psOperand->psSubOperand[subOperand] != 0) + { + Operand* psSubOperand = psOperand->psSubOperand[subOperand]; + METALWriteOperandTypes(psSubOperand, aeTempVecType); + if (psSubOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + METALSetCBOperandComponents(psContext, psSubOperand); + } + } + } + + //Set immediates + if (METALIsIntegerImmediateOpcode(psInst->eOpcode)) + { + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32) + { + psOperand->iIntegerImmediate = 1; + } + } + } + + //Process the destination last in order to handle instructions + //where the destination register is also used as a source. + for (k = 0; k < (int)psInst->ui32FirstSrc; ++k) + { + Operand* psOperand = &psInst->asOperands[k]; + METALWriteOperandTypes(psOperand, aeTempVecType); + } + } +} + +void DetectAtomicInstructionMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, Instruction* psNextInst, AtomicVarList* psAtomicList) +{ + (void)psNextInst; + + Operand* dest = 0; + Operand* destAddr = 0; + + switch (psInst->eOpcode) + { + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_ATOMIC_AND: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMIN: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_IMAX: + dest = &psInst->asOperands[0]; + destAddr = &psInst->asOperands[1]; + break; + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + case OPCODE_IMM_ATOMIC_AND: + dest = &psInst->asOperands[1]; + destAddr = &psInst->asOperands[2]; + break; + default: + return; + } + + if (dest->eType == OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY) + { + } + else + { + ResourceBinding* psRes; +#if defined(_DEBUG) + int foundResource = +#endif + GetResourceFromBindingPoint(RGROUP_UAV, + dest->ui32RegisterNumber, + &psContext->psShader->sInfo, + &psRes); + + ASSERT(foundResource); + + { + //rwbuffer + if (psRes->eType == RTYPE_UAV_RWTYPED) + { + } + //rwstructured buffer + else if (psRes->eType == RTYPE_UAV_RWSTRUCTURED) + { + if (destAddr->eType == OPERAND_TYPE_IMMEDIATE32) + { + psAtomicList->AtomicVars[psAtomicList->Filled] = METALLookupStructuredVarAtomic(psContext, dest, destAddr, 0); + psAtomicList->Filled++; + } + } + } + } +} + +void TranslateInstructionMETAL(HLSLCrossCompilerContext* psContext, Instruction* psInst, Instruction* psNextInst) +{ + bstring metal = *psContext->currentShaderString; + int numParenthesis = 0; + +#ifdef _DEBUG + AddIndentation(psContext); + bformata(metal, "//Instruction %d\n", psInst->id); +#if 0 + if (psInst->id == 73) + { + ASSERT(1); //Set breakpoint here to debug an instruction from its ID. + } +#endif +#endif + + switch (psInst->eOpcode) + { + case OPCODE_FTOI: + case OPCODE_FTOU: + { + uint32_t dstCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + uint32_t srcCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[1]); + +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_FTOU) + { + bcatcstr(metal, "//FTOU\n"); + } + else + { + bcatcstr(metal, "//FTOI\n"); + } +#endif + + AddIndentation(psContext); + + METALAddAssignToDest(psContext, &psInst->asOperands[0], psInst->eOpcode == OPCODE_FTOU ? SVT_UINT : SVT_INT, srcCount, &numParenthesis); + bcatcstr(metal, GetConstructorForTypeMETAL(psInst->eOpcode == OPCODE_FTOU ? SVT_UINT : SVT_INT, srcCount == dstCount ? dstCount : 4)); + bcatcstr(metal, "("); // 1 + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(metal, ")"); // 1 + // Add destination writemask if the component counts do not match + if (srcCount != dstCount) + { + AddSwizzleUsingElementCountMETAL(psContext, dstCount); + } + METALAddAssignPrologue(psContext, numParenthesis); + break; + } + + case OPCODE_MOV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//MOV\n"); +#endif + AddIndentation(psContext); + METALAddMOVBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[1]); + break; + } + case OPCODE_ITOF://signed to float + case OPCODE_UTOF://unsigned to float + { + uint32_t dstCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + uint32_t srcCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[1]); + uint32_t destMask = GetOperandWriteMaskMETAL(&psInst->asOperands[0]); + +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_ITOF) + { + bcatcstr(metal, "//ITOF\n"); + } + else + { + bcatcstr(metal, "//UTOF\n"); + } +#endif + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, srcCount, &numParenthesis); + bcatcstr(metal, GetConstructorForTypeMETAL(SVT_FLOAT, dstCount)); + bcatcstr(metal, "("); // 1 + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[1], psInst->eOpcode == OPCODE_UTOF ? TO_AUTO_BITCAST_TO_UINT : TO_AUTO_BITCAST_TO_INT, destMask); + bcatcstr(metal, ")"); // 1 + // Add destination writemask if the component counts do not match + if (srcCount != dstCount) + { + AddSwizzleUsingElementCountMETAL(psContext, dstCount); + } + METALAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_MAD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//MAD\n"); +#endif + METALCallTernaryOp(psContext, "*", "+", psInst, 0, 1, 2, 3, TO_FLAG_NONE); + break; + } + case OPCODE_IMAD: + { + uint32_t ui32Flags = TO_FLAG_INTEGER; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMAD\n"); +#endif + + if (GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + ui32Flags = TO_FLAG_UNSIGNED_INTEGER; + } + + METALCallTernaryOp(psContext, "*", "+", psInst, 0, 1, 2, 3, ui32Flags); + break; + } + case OPCODE_DADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DADD\n"); +#endif + METALCallBinaryOp(psContext, "+", psInst, 0, 1, 2, SVT_DOUBLE); + break; + } + case OPCODE_IADD: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IADD\n"); +#endif + //Is this a signed or unsigned add? + if (GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + eType = SVT_UINT; + } + METALCallBinaryOp(psContext, "+", psInst, 0, 1, 2, eType); + break; + } + case OPCODE_ADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ADD\n"); +#endif + METALCallBinaryOp(psContext, "+", psInst, 0, 1, 2, SVT_FLOAT); + break; + } + case OPCODE_OR: + { + /*Todo: vector version */ +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//OR\n"); +#endif + METALCallBinaryOp(psContext, "|", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_AND: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//AND\n"); +#endif + METALCallBinaryOp(psContext, "&", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_GE: + { + /* + dest = vec4(greaterThanEqual(vec4(srcA), vec4(srcB)); + Caveat: The result is a boolean but HLSL asm returns 0xFFFFFFFF/0x0 instead. + */ +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//GE\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_GE, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_MUL: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//MUL\n"); +#endif + METALCallBinaryOp(psContext, "*", psInst, 0, 1, 2, SVT_FLOAT); + break; + } + case OPCODE_IMUL: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMUL\n"); +#endif + if (GetOperandDataTypeMETAL(psContext, &psInst->asOperands[1]) == SVT_UINT) + { + eType = SVT_UINT; + } + + ASSERT(psInst->asOperands[0].eType == OPERAND_TYPE_NULL); + + METALCallBinaryOp(psContext, "*", psInst, 1, 2, 3, eType); + break; + } + case OPCODE_UDIV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//UDIV\n"); +#endif + //destQuotient, destRemainder, src0, src1 + METALCallBinaryOp(psContext, "/", psInst, 0, 2, 3, SVT_UINT); + METALCallBinaryOp(psContext, "%", psInst, 1, 2, 3, SVT_UINT); + break; + } + case OPCODE_DIV: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DIV\n"); +#endif + METALCallBinaryOp(psContext, "/", psInst, 0, 1, 2, SVT_FLOAT); + break; + } + case OPCODE_SINCOS: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SINCOS\n"); +#endif + // Need careful ordering if src == dest[0], as then the cos() will be reading from wrong value + if (psInst->asOperands[0].eType == psInst->asOperands[2].eType && + psInst->asOperands[0].ui32RegisterNumber == psInst->asOperands[2].ui32RegisterNumber) + { + // sin() result overwrites source, do cos() first. + // The case where both write the src shouldn't really happen anyway. + if (psInst->asOperands[1].eType != OPERAND_TYPE_NULL) + { + METALCallHelper1(psContext, "cos", psInst, 1, 2, 1); + } + + if (psInst->asOperands[0].eType != OPERAND_TYPE_NULL) + { + METALCallHelper1(psContext, "sin", psInst, 0, 2, 1); + } + } + else + { + if (psInst->asOperands[0].eType != OPERAND_TYPE_NULL) + { + METALCallHelper1(psContext, "sin", psInst, 0, 2, 1); + } + + if (psInst->asOperands[1].eType != OPERAND_TYPE_NULL) + { + METALCallHelper1(psContext, "cos", psInst, 1, 2, 1); + } + } + break; + } + + case OPCODE_DP2: + { + SHADER_VARIABLE_TYPE eDestDataType = GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]); + int numParenthesis2 = 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DP2\n"); +#endif + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, 1, &numParenthesis2); + bcatcstr(metal, "dot("); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[1], TO_AUTO_BITCAST_TO_FLOAT | SVTTypeToFlagMETAL(eDestDataType), 3 /* .xy */); + bcatcstr(metal, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[2], TO_AUTO_BITCAST_TO_FLOAT | SVTTypeToFlagMETAL(eDestDataType), 3 /* .xy */); + bcatcstr(metal, ")"); + METALAddAssignPrologue(psContext, numParenthesis2); + break; + } + case OPCODE_DP3: + { + SHADER_VARIABLE_TYPE eDestDataType = GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]); + int numParenthesis2 = 0; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DP3\n"); +#endif + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, 1, &numParenthesis2); + bcatcstr(metal, "dot("); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[1], TO_AUTO_BITCAST_TO_FLOAT | SVTTypeToFlagMETAL(eDestDataType), 7 /* .xyz */); + bcatcstr(metal, ", "); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[2], TO_AUTO_BITCAST_TO_FLOAT | SVTTypeToFlagMETAL(eDestDataType), 7 /* .xyz */); + bcatcstr(metal, ")"); + METALAddAssignPrologue(psContext, numParenthesis2); + break; + } + case OPCODE_DP4: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DP4\n"); +#endif + METALCallHelper2(psContext, "dot", psInst, 0, 1, 2, 0); + break; + } + case OPCODE_INE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//INE\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_NE, TO_FLAG_INTEGER, NULL); + break; + } + case OPCODE_NE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//NE\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_NE, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_IGE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IGE\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_GE, TO_FLAG_INTEGER, psNextInst); + break; + } + case OPCODE_ILT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ILT\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_LT, TO_FLAG_INTEGER, NULL); + break; + } + case OPCODE_LT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LT\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_LT, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_IEQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IEQ\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_EQ, TO_FLAG_INTEGER, NULL); + break; + } + case OPCODE_ULT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ULT\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_LT, TO_FLAG_UNSIGNED_INTEGER, NULL); + break; + } + case OPCODE_UGE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//UGE\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_GE, TO_FLAG_UNSIGNED_INTEGER, NULL); + break; + } + case OPCODE_MOVC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//MOVC\n"); +#endif + METALAddMOVCBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[1], &psInst->asOperands[2], &psInst->asOperands[3]); + break; + } + case OPCODE_SWAPC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SWAPC\n"); +#endif + // TODO needs temps!! + METALAddMOVCBinaryOp(psContext, &psInst->asOperands[0], &psInst->asOperands[2], &psInst->asOperands[4], &psInst->asOperands[3]); + METALAddMOVCBinaryOp(psContext, &psInst->asOperands[1], &psInst->asOperands[2], &psInst->asOperands[3], &psInst->asOperands[4]); + break; + } + + case OPCODE_LOG: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LOG\n"); +#endif + METALCallHelper1(psContext, "log2", psInst, 0, 1, 1); + break; + } + case OPCODE_RSQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//RSQ\n"); +#endif + METALCallHelper1(psContext, "rsqrt", psInst, 0, 1, 1); + break; + } + case OPCODE_EXP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//EXP\n"); +#endif + METALCallHelper1(psContext, "exp2", psInst, 0, 1, 1); + break; + } + case OPCODE_SQRT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SQRT\n"); +#endif + METALCallHelper1(psContext, "sqrt", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_PI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ROUND_PI\n"); +#endif + METALCallHelper1(psContext, "ceil", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_NI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ROUND_NI\n"); +#endif + METALCallHelper1(psContext, "floor", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_Z: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ROUND_Z\n"); +#endif + METALCallHelper1(psContext, "trunc", psInst, 0, 1, 1); + break; + } + case OPCODE_ROUND_NE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ROUND_NE\n"); +#endif + METALCallHelper1(psContext, "rint", psInst, 0, 1, 1); + break; + } + case OPCODE_FRC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//FRC\n"); +#endif + METALCallHelper1(psContext, "fract", psInst, 0, 1, 1); + break; + } + case OPCODE_IMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMAX\n"); +#endif + METALCallHelper2Int(psContext, "max", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_MAX: + case OPCODE_UMAX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//MAX\n"); +#endif + METALCallHelper2(psContext, "max", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_IMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IMIN\n"); +#endif + METALCallHelper2Int(psContext, "min", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_MIN: + case OPCODE_UMIN: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//MIN\n"); +#endif + METALCallHelper2(psContext, "min", psInst, 0, 1, 2, 1); + break; + } + case OPCODE_GATHER4: + case OPCODE_GATHER4_C: + { + //dest, coords, tex, sampler + const RESOURCE_DIMENSION eResDim = psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber]; + +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_GATHER4_C) + { + bcatcstr(metal, "//GATHER4_C\n"); + } + else + { + bcatcstr(metal, "//GATHER4\n"); + } +#endif + //gather4 r7.xyzw, r3.xyxx, t3.xyzw, s0.x + AddIndentation(psContext); // TODO FIXME integer samplers + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, GetNumSwizzleElementsMETAL(&psInst->asOperands[2]), &numParenthesis); + bcatcstr(metal, "("); + + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, 0); + + bcatcstr(metal, ".gather("); + bconcat(metal, TextureSamplerNameMETAL(&psContext->psShader->sInfo, psInst->asOperands[2].ui32RegisterNumber, psInst->asOperands[3].ui32RegisterNumber, psInst->eOpcode == OPCODE_GATHER4_PO_C)); + bcatcstr(metal, ", "); + METALTranslateTexCoord(psContext, eResDim, &psInst->asOperands[1]); + + if (psInst->eOpcode == OPCODE_GATHER4_C) + { + bcatcstr(metal, ", "); + TranslateOperandMETAL(psContext, &psInst->asOperands[4], TO_FLAG_NONE); + } + bcatcstr(metal, ")"); + + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[2]); + bcatcstr(metal, ")"); + + AddSwizzleUsingElementCountMETAL(psContext, GetNumSwizzleElementsMETAL(&psInst->asOperands[0])); + METALAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_GATHER4_PO: + case OPCODE_GATHER4_PO_C: + { + //dest, coords, offset, tex, sampler, srcReferenceValue + +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_GATHER4_PO_C) + { + bcatcstr(metal, "//GATHER4_PO_C\n"); + } + else + { + bcatcstr(metal, "//GATHER4_PO\n"); + } +#endif + + AddIndentation(psContext); // TODO FIXME integer samplers + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, GetNumSwizzleElementsMETAL(&psInst->asOperands[2]), &numParenthesis); + bcatcstr(metal, "("); + + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psInst->asOperands[3].ui32RegisterNumber, 0); + + bcatcstr(metal, ".gather("); + bconcat(metal, TextureSamplerNameMETAL(&psContext->psShader->sInfo, psInst->asOperands[3].ui32RegisterNumber, psInst->asOperands[4].ui32RegisterNumber, psInst->eOpcode == OPCODE_GATHER4_PO_C)); + + bcatcstr(metal, ", "); + //Texture coord cannot be vec4 + //Determining if it is a vec3 for vec2 yet to be done. + psInst->asOperands[1].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[1].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + + if (psInst->eOpcode == OPCODE_GATHER4_PO_C) + { + bcatcstr(metal, ", "); + TranslateOperandMETAL(psContext, &psInst->asOperands[5], TO_FLAG_NONE); + } + + bcatcstr(metal, ", as_type("); + //ivec2 offset + psInst->asOperands[2].aui32Swizzle[2] = 0xFFFFFFFF; + psInst->asOperands[2].aui32Swizzle[3] = 0xFFFFFFFF; + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, "))"); + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleMETAL(psContext, &psInst->asOperands[3]); + bcatcstr(metal, ")"); + + AddSwizzleUsingElementCountMETAL(psContext, GetNumSwizzleElementsMETAL(&psInst->asOperands[0])); + METALAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_SAMPLE: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SAMPLE\n"); +#endif + METALTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_NONE); + break; + } + case OPCODE_SAMPLE_L: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SAMPLE_L\n"); +#endif + METALTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_LOD); + break; + } + case OPCODE_SAMPLE_C: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SAMPLE_C\n"); +#endif + + METALTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_DEPTHCOMPARE); + break; + } + case OPCODE_SAMPLE_C_LZ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SAMPLE_C_LZ\n"); +#endif + + METALTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_DEPTHCOMPARE | TEXSMP_FLAG_FIRSTLOD); + break; + } + case OPCODE_SAMPLE_D: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SAMPLE_D\n"); +#endif + + METALTranslateTextureSample(psContext, psInst, TEXSMP_FLAGS_GRAD); + break; + } + case OPCODE_SAMPLE_B: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SAMPLE_B\n"); +#endif + + METALTranslateTextureSample(psContext, psInst, TEXSMP_FLAG_BIAS); + break; + } + case OPCODE_RET: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//RET\n"); +#endif + if (psContext->havePostShaderCode[psContext->currentPhase]) + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//--- Post shader code ---\n"); +#endif + bconcat(metal, psContext->postShaderCode[psContext->currentPhase]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//--- End post shader code ---\n"); +#endif + } + AddIndentation(psContext); + if (blength(psContext->declaredOutputs) > 0) + { + //has output + bcatcstr(metal, "return output;\n"); + } + else + { + //no output declared + bcatcstr(metal, "return;\n"); + } + break; + } + case OPCODE_INTERFACE_CALL: + { + const char* name; + ShaderVar* psVar; + uint32_t varFound; + + uint32_t funcPointer; + uint32_t funcTableIndex; + uint32_t funcTable; + uint32_t funcBodyIndex; + uint32_t funcBody; + uint32_t ui32NumBodiesPerTable; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//INTERFACE_CALL\n"); +#endif + + ASSERT(psInst->asOperands[0].eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32); + + funcPointer = psInst->asOperands[0].aui32ArraySizes[0]; + funcTableIndex = psInst->asOperands[0].aui32ArraySizes[1]; + funcBodyIndex = psInst->ui32FuncIndexWithinInterface; + + ui32NumBodiesPerTable = psContext->psShader->funcPointer[funcPointer].ui32NumBodiesPerTable; + + funcTable = psContext->psShader->funcPointer[funcPointer].aui32FuncTables[funcTableIndex]; + + funcBody = psContext->psShader->funcTable[funcTable].aui32FuncBodies[funcBodyIndex]; + + varFound = GetInterfaceVarFromOffset(funcPointer, &psContext->psShader->sInfo, &psVar); + + ASSERT(varFound); + + name = &psVar->Name[0]; + + AddIndentation(psContext); + bcatcstr(metal, name); + TranslateOperandIndexMADMETAL(psContext, &psInst->asOperands[0], 1, ui32NumBodiesPerTable, funcBodyIndex); + //bformata(glsl, "[%d]", funcBodyIndex); + bcatcstr(metal, "();\n"); + break; + } + case OPCODE_LABEL: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LABEL\n"); +#endif + --psContext->indent; + AddIndentation(psContext); + bcatcstr(metal, "}\n"); //Closing brace ends the previous function. + AddIndentation(psContext); + + bcatcstr(metal, "subroutine(SubroutineType)\n"); + bcatcstr(metal, "void "); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(metal, "(){\n"); + ++psContext->indent; + break; + } + case OPCODE_COUNTBITS: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//COUNTBITS\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(metal, " = popcount("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(metal, ");\n"); + break; + } + case OPCODE_FIRSTBIT_HI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//FIRSTBIT_HI\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(metal, " = (32 - clz("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, "));\n"); + break; + } + case OPCODE_FIRSTBIT_LO: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//FIRSTBIT_LO\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_UNSIGNED_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(metal, " = (1 + ctz("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ")));\n"); + break; + } + case OPCODE_FIRSTBIT_SHI: //signed high + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//FIRSTBIT_SHI\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER | TO_FLAG_DESTINATION); + bcatcstr(metal, " = (32 - clz("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(metal, " > 0 ? "); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(metal, " : 0xFFFFFFFF ^ "); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER); + bcatcstr(metal, ")));\n"); + break; + } + case OPCODE_BFI: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//BFI\n"); +#endif + // This instruction is not available in Metal shading language. + // Need to expend it out (http://http.developer.nvidia.com/Cg/bitfieldInsert.html) + + int numComponents = psInst->asOperands[0].iNumComponents; + + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(metal, " = 0;\n"); + + AddIndentation(psContext); + bcatcstr(metal, "{\n"); + + AddIndentation(psContext); + bformata(metal, " %s mask = ~(%s(0xffffffff) << ", GetConstructorForTypeMETAL(SVT_UINT, numComponents), GetConstructorForTypeMETAL(SVT_UINT, numComponents)); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ") << "); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ";\n"); + + AddIndentation(psContext); + bcatcstr(metal, " mask = ~mask;\n"); + + AddIndentation(psContext); + bcatcstr(metal, " "); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bformata(metal, " = ( as_type<%s>( (", GetConstructorForTypeMETAL(psInst->asOperands[0].aeDataType[0], numComponents)); + TranslateOperandMETAL(psContext, &psInst->asOperands[4], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, " & mask) | ("); + TranslateOperandMETAL(psContext, &psInst->asOperands[3], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, " << "); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ")) )"); + TranslateOperandSwizzleWithMaskMETAL(psContext, &psInst->asOperands[0], GetOperandWriteMaskMETAL(&psInst->asOperands[0])); + bcatcstr(metal, ";\n"); + + AddIndentation(psContext); + bcatcstr(metal, "}\n"); + + + + break; + } + case OPCODE_BFREV: + case OPCODE_CUT: + case OPCODE_EMIT: + case OPCODE_EMITTHENCUT: + case OPCODE_CUT_STREAM: + case OPCODE_EMIT_STREAM: + case OPCODE_EMITTHENCUT_STREAM: + { + // not implemented in metal + ASSERT(0); + break; + } + case OPCODE_REP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//REP\n"); +#endif + //Need to handle nesting. + //Max of 4 for rep - 'Flow Control Limitations' http://msdn.microsoft.com/en-us/library/windows/desktop/bb219848(v=vs.85).aspx + + AddIndentation(psContext); + bcatcstr(metal, "RepCounter = as_type("); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(metal, ").x;\n"); + + AddIndentation(psContext); + bcatcstr(metal, "while(RepCounter!=0){\n"); + ++psContext->indent; + break; + } + case OPCODE_ENDREP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ENDREP\n"); +#endif + AddIndentation(psContext); + bcatcstr(metal, "RepCounter--;\n"); + + --psContext->indent; + + AddIndentation(psContext); + bcatcstr(metal, "}\n"); + break; + } + case OPCODE_LOOP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LOOP\n"); +#endif + AddIndentation(psContext); + + if (psInst->ui32NumOperands == 2) + { + //DX9 version + ASSERT(psInst->asOperands[0].eType == OPERAND_TYPE_SPECIAL_LOOPCOUNTER); + bcatcstr(metal, "for("); + bcatcstr(metal, "LoopCounter = "); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(metal, ".y, ZeroBasedCounter = 0;"); + bcatcstr(metal, "ZeroBasedCounter < "); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(metal, ".x;"); + + bcatcstr(metal, "LoopCounter += "); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(metal, ".z, ZeroBasedCounter++){\n"); + ++psContext->indent; + } + else + { + bcatcstr(metal, "while(true){\n"); + ++psContext->indent; + } + break; + } + case OPCODE_ENDLOOP: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ENDLOOP\n"); +#endif + AddIndentation(psContext); + bcatcstr(metal, "}\n"); + break; + } + case OPCODE_BREAK: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//BREAK\n"); +#endif + AddIndentation(psContext); + bcatcstr(metal, "break;\n"); + break; + } + case OPCODE_BREAKC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//BREAKC\n"); +#endif + AddIndentation(psContext); + + METALTranslateConditional(psContext, psInst, metal); + break; + } + case OPCODE_CONTINUEC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//CONTINUEC\n"); +#endif + AddIndentation(psContext); + + METALTranslateConditional(psContext, psInst, metal); + break; + } + case OPCODE_IF: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//IF\n"); +#endif + AddIndentation(psContext); + + METALTranslateConditional(psContext, psInst, metal); + ++psContext->indent; + break; + } + case OPCODE_RETC: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//RETC\n"); +#endif + AddIndentation(psContext); + + METALTranslateConditional(psContext, psInst, metal); + break; + } + case OPCODE_ELSE: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ELSE\n"); +#endif + AddIndentation(psContext); + bcatcstr(metal, "} else {\n"); + psContext->indent++; + break; + } + case OPCODE_ENDSWITCH: + case OPCODE_ENDIF: + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(metal, "//ENDIF\n"); + AddIndentation(psContext); + bcatcstr(metal, "}\n"); + break; + } + case OPCODE_CONTINUE: + { + AddIndentation(psContext); + bcatcstr(metal, "continue;\n"); + break; + } + case OPCODE_DEFAULT: + { + --psContext->indent; + AddIndentation(psContext); + bcatcstr(metal, "default:\n"); + ++psContext->indent; + break; + } + case OPCODE_NOP: + { + break; + } + case OPCODE_SYNC: + { + const uint32_t ui32SyncFlags = psInst->ui32SyncFlags; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SYNC\n"); +#endif + // warning. Although Metal documentation claims the flag can be combined + // this is not true in terms of binary operations. One can't simply OR flags + // but rather have to use pre-defined literals. + char* aszBarrierType[] = { + "mem_flags::mem_none", + "mem_flags::mem_threadgroup", + "mem_flags::mem_device", + "mem_flags::mem_device_and_threadgroup" + }; + typedef enum + { + BT_None, + BT_MemThreadGroup, + BT_MemDevice, + BT_MemDeviceAndMemThreadGroup + } BT; + BT barrierType = BT_None; + + if (ui32SyncFlags & SYNC_THREADS_IN_GROUP) + { + AddIndentation(psContext); + bcatcstr(metal, "threadgroup_barrier("); + } + else + { + AddIndentation(psContext); + // simdgroup_barrier is faster than threadgroup_barrier. It is supported on iOS 10+ on all hardware. + bcatcstr(metal, "threadgroup_barrier("); + } + + if (ui32SyncFlags & SYNC_THREAD_GROUP_SHARED_MEMORY) + { + barrierType = (BT)(barrierType | BT_MemThreadGroup); + } + if (ui32SyncFlags & (SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP | SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GLOBAL)) + { + barrierType = (BT)(barrierType | BT_MemDevice); + } + + bcatcstr(metal, aszBarrierType[barrierType]); + bcatcstr(metal, ");\n"); + + break; + } + case OPCODE_SWITCH: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//SWITCH\n"); +#endif + AddIndentation(psContext); + bcatcstr(metal, "switch(int("); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(metal, ")){\n"); + + psContext->indent += 2; + break; + } + case OPCODE_CASE: + { + --psContext->indent; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//case\n"); +#endif + AddIndentation(psContext); + + bcatcstr(metal, "case "); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(metal, ":\n"); + + ++psContext->indent; + break; + } + case OPCODE_EQ: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//EQ\n"); +#endif + METALAddComparision(psContext, psInst, METAL_CMP_EQ, TO_FLAG_NONE, NULL); + break; + } + case OPCODE_USHR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//USHR\n"); +#endif + METALCallBinaryOp(psContext, ">>", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_ISHL: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ISHL\n"); +#endif + + if (GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + eType = SVT_UINT; + } + + METALCallBinaryOp(psContext, "<<", psInst, 0, 1, 2, eType); + break; + } + case OPCODE_ISHR: + { + SHADER_VARIABLE_TYPE eType = SVT_INT; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//ISHR\n"); +#endif + + if (GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]) == SVT_UINT) + { + eType = SVT_UINT; + } + + METALCallBinaryOp(psContext, ">>", psInst, 0, 1, 2, eType); + break; + } + case OPCODE_LD: + case OPCODE_LD_MS: + { + ResourceBinding* psBinding = 0; +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_LD) + { + bcatcstr(metal, "//LD\n"); + } + else + { + bcatcstr(metal, "//LD_MS\n"); + } +#endif + + GetResourceFromBindingPoint(RGROUP_TEXTURE, psInst->asOperands[2].ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + + //if (psInst->bAddressOffset) + //{ + // METALTranslateTexelFetchOffset(psContext, psInst, psBinding, metal); + //} + //else + //{ + METALTranslateTexelFetch(psContext, psInst, psBinding, metal); + //} + break; + } + case OPCODE_DISCARD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DISCARD\n"); +#endif + AddIndentation(psContext); + + if (psInst->eBooleanTestType == INSTRUCTION_TEST_ZERO) + { + bcatcstr(metal, "if(all("); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(metal, "==0)){discard_fragment();}\n"); + } + else + { + ASSERT(psInst->eBooleanTestType == INSTRUCTION_TEST_NONZERO); + bcatcstr(metal, "if(any("); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_INTEGER); + bcatcstr(metal, "!=0)){discard_fragment();}\n"); + } + break; + } + case OPCODE_LOD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LOD\n"); +#endif + //LOD computes the following vector (ClampedLOD, NonClampedLOD, 0, 0) + + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, 4, &numParenthesis); + + //If the core language does not have query-lod feature, + //then the extension is used. The name of the function + //changed between extension and core. + if (HaveQueryLod(psContext->psShader->eTargetLanguage)) + { + bcatcstr(metal, "textureQueryLod("); + } + else + { + bcatcstr(metal, "textureQueryLOD("); + } + + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ","); + METALTranslateTexCoord(psContext, + psContext->psShader->aeResourceDims[psInst->asOperands[2].ui32RegisterNumber], + &psInst->asOperands[1]); + bcatcstr(metal, ")"); + + //The swizzle on srcResource allows the returned values to be swizzled arbitrarily before they are written to the destination. + + // iWriteMaskEnabled is forced off during DecodeOperand because swizzle on sampler uniforms + // does not make sense. But need to re-enable to correctly swizzle this particular instruction. + psInst->asOperands[2].iWriteMaskEnabled = 1; + TranslateOperandSwizzleWithMaskMETAL(psContext, &psInst->asOperands[2], GetOperandWriteMaskMETAL(&psInst->asOperands[0])); + METALAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_EVAL_CENTROID: + case OPCODE_EVAL_SAMPLE_INDEX: + case OPCODE_EVAL_SNAPPED: + { + // ERROR: evaluation functions are not implemented in metal + ASSERT(0); + break; + } + case OPCODE_LD_STRUCTURED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LD_STRUCTURED\n"); +#endif + METALTranslateShaderStorageLoad(psContext, psInst); + break; + } + case OPCODE_LD_UAV_TYPED: + { + // not implemented in metal + ASSERT(0); + break; + } + case OPCODE_STORE_RAW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//STORE_RAW\n"); +#endif + METALTranslateShaderStorageStore(psContext, psInst); + break; + } + case OPCODE_STORE_STRUCTURED: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//STORE_STRUCTURED\n"); +#endif + METALTranslateShaderStorageStore(psContext, psInst); + break; + } + + case OPCODE_STORE_UAV_TYPED: + { + ResourceBinding* psRes; + int foundResource; + +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//STORE_UAV_TYPED\n"); +#endif + AddIndentation(psContext); + + foundResource = GetResourceFromBindingPoint(RGROUP_UAV, + psInst->asOperands[0].ui32RegisterNumber, + &psContext->psShader->sInfo, + &psRes); + + ASSERT(foundResource); + + if (psRes->eBindArea == UAVAREA_CBUFFER) + { + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_NAME_ONLY); + bcatcstr(metal, "["); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(metal, "]="); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[2], METALResourceReturnTypeToFlag(psRes->ui32ReturnType), OPERAND_4_COMPONENT_MASK_X); + bcatcstr(metal, ";\n"); + } + else if (psRes->eBindArea == UAVAREA_TEXTURE) + { + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".write("); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[2], METALResourceReturnTypeToFlag(psRes->ui32ReturnType), OPERAND_4_COMPONENT_MASK_ALL); + switch (psRes->eDimension) + { + case REFLECT_RESOURCE_DIMENSION_TEXTURE1D: + { + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ") "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2D: + { + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".xy) "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE1DARRAY: + { + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".x) "); + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".y) "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DARRAY: + { + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".xy) "); + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".z) "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE3D: + { + bcatcstr(metal, ", as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".xyz) "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBE: + { + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".xy) "); + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".z) "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURECUBEARRAY: + { + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".xy) "); + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".z) "); + bcatcstr(metal, ",as_type("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NAME_ONLY); + bcatcstr(metal, ".w) "); + break; + } + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMS: + case REFLECT_RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + //not supported in mnetal + ASSERT(0); + break; + } + ; + bcatcstr(metal, ");\n"); + } + else + { + //UAV is not exist in either [[buffer]] or [[texture]] + ASSERT(0); + } + break; + } + case OPCODE_LD_RAW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LD_RAW\n"); +#endif + + METALTranslateShaderStorageLoad(psContext, psInst); + break; + } + + case OPCODE_ATOMIC_CMP_STORE: + case OPCODE_IMM_ATOMIC_AND: + case OPCODE_ATOMIC_AND: + case OPCODE_IMM_ATOMIC_IADD: + case OPCODE_ATOMIC_IADD: + case OPCODE_ATOMIC_OR: + case OPCODE_ATOMIC_XOR: + case OPCODE_ATOMIC_IMIN: + case OPCODE_ATOMIC_UMIN: + case OPCODE_ATOMIC_UMAX: + case OPCODE_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMAX: + case OPCODE_IMM_ATOMIC_IMIN: + case OPCODE_IMM_ATOMIC_UMAX: + case OPCODE_IMM_ATOMIC_UMIN: + case OPCODE_IMM_ATOMIC_OR: + case OPCODE_IMM_ATOMIC_XOR: + case OPCODE_IMM_ATOMIC_EXCH: + case OPCODE_IMM_ATOMIC_CMP_EXCH: + { + TranslateAtomicMemOpMETAL(psContext, psInst); + break; + } + case OPCODE_UBFE: + case OPCODE_IBFE: + { +#ifdef _DEBUG + AddIndentation(psContext); + if (psInst->eOpcode == OPCODE_UBFE) + { + bcatcstr(metal, "//OPCODE_UBFE\n"); + } + else + { + bcatcstr(metal, "//OPCODE_IBFE\n"); + } +#endif + // These instructions are not available in Metal shading language. + // Need to expend it out (http://http.developer.nvidia.com/Cg/bitfieldExtract.html) + // NOTE: we assume bitoffset is always > 0 as to avoid dynamic branching. + // NOTE: We have taken out the -1 as this was breaking the GPU particles bitfields. + + int numComponents = psInst->asOperands[0].iNumComponents; + + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(metal, " = 0;\n"); + + AddIndentation(psContext); + bcatcstr(metal, "{\n"); + + AddIndentation(psContext); + bformata(metal, " %s mask = ~(%s(0xffffffff) << ", GetConstructorForTypeMETAL(SVT_UINT, numComponents), GetConstructorForTypeMETAL(SVT_UINT, numComponents)); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ");\n"); + + AddIndentation(psContext); + bcatcstr(metal, " "); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bformata(metal, " = ( as_type<%s>((", GetConstructorForTypeMETAL(psInst->asOperands[0].aeDataType[0], numComponents)); + TranslateOperandMETAL(psContext, &psInst->asOperands[3], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, " >> ( "); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_UNSIGNED_INTEGER); + bcatcstr(metal, ")) & mask) )"); + TranslateOperandSwizzleWithMaskMETAL(psContext, &psInst->asOperands[0], GetOperandWriteMaskMETAL(&psInst->asOperands[0])); + bcatcstr(metal, ";\n"); + + AddIndentation(psContext); + bcatcstr(metal, "}\n"); + + break; + } + case OPCODE_RCP: + { + const uint32_t destElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//RCP\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(metal, " = (float4(1.0) / float4("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(metal, "))"); + AddSwizzleUsingElementCountMETAL(psContext, destElemCount); + bcatcstr(metal, ";\n"); + break; + } + case OPCODE_F32TOF16: + { + const uint32_t destElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[1]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//F32TOF16\n"); +#endif + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = { ".x", ".y", ".z", ".w" }; + + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + if (destElemCount > 1) + { + bcatcstr(metal, swizzle[destElem]); + } + + bcatcstr(metal, " = "); + + SHADER_VARIABLE_TYPE eDestDataType = GetOperandDataTypeMETAL(psContext, &psInst->asOperands[0]); + if (SVT_FLOAT == eDestDataType) + { + bcatcstr(metal, "as_type"); + } + bcatcstr(metal, "( (uint( as_type( (half)"); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + if (s0ElemCount > 1) + { + bcatcstr(metal, swizzle[destElem]); + } + bcatcstr(metal, " ) ) ) );\n"); + } + break; + } + case OPCODE_F16TOF32: + { + const uint32_t destElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + const uint32_t s0ElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[1]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//F16TOF32\n"); +#endif + for (destElem = 0; destElem < destElemCount; ++destElem) + { + const char* swizzle[] = { ".x", ".y", ".z", ".w" }; + + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION | TO_FLAG_UNSIGNED_INTEGER); + if (destElemCount > 1) + { + bcatcstr(metal, swizzle[destElem]); + } + + bcatcstr(metal, " = as_type ((unsigned short)"); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_UNSIGNED_INTEGER); + if (s0ElemCount > 1) + { + bcatcstr(metal, swizzle[destElem]); + } + bcatcstr(metal, ");\n"); + } + break; + } + case OPCODE_INEG: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//INEG\n"); +#endif + uint32_t dstCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + uint32_t srcCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[1]); + + //dest = 0 - src0 + bcatcstr(metal, "-("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE | TO_FLAG_INTEGER); + if (srcCount > dstCount) + { + AddSwizzleUsingElementCountMETAL(psContext, dstCount); + } + bcatcstr(metal, ")"); + bcatcstr(metal, ";\n"); + break; + } + case OPCODE_DERIV_RTX_COARSE: + case OPCODE_DERIV_RTX_FINE: + case OPCODE_DERIV_RTX: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DERIV_RTX\n"); +#endif + METALCallHelper1(psContext, "dfdx", psInst, 0, 1, 1); + break; + } + case OPCODE_DERIV_RTY_COARSE: + case OPCODE_DERIV_RTY_FINE: + case OPCODE_DERIV_RTY: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DERIV_RTY\n"); +#endif + METALCallHelper1(psContext, "dfdy", psInst, 0, 1, 1); + break; + } + case OPCODE_LRP: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//LRP\n"); +#endif + METALCallHelper3(psContext, "mix", psInst, 0, 2, 3, 1, 1); + break; + } + case OPCODE_DP2ADD: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//DP2ADD\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(metal, " = dot(float2("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(metal, "), float2("); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ")) + "); + TranslateOperandMETAL(psContext, &psInst->asOperands[3], TO_FLAG_NONE); + bcatcstr(metal, ";\n"); + break; + } + case OPCODE_POW: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//POW\n"); +#endif + AddIndentation(psContext); + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_FLAG_DESTINATION); + bcatcstr(metal, " = pow(abs("); + TranslateOperandMETAL(psContext, &psInst->asOperands[1], TO_FLAG_NONE); + bcatcstr(metal, "), "); + TranslateOperandMETAL(psContext, &psInst->asOperands[2], TO_FLAG_NONE); + bcatcstr(metal, ");\n"); + break; + } + + case OPCODE_IMM_ATOMIC_ALLOC: + case OPCODE_IMM_ATOMIC_CONSUME: + { + // not implemented in metal + ASSERT(0); + break; + } + + case OPCODE_NOT: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//INOT\n"); +#endif + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_INT, GetNumSwizzleElementsMETAL(&psInst->asOperands[1]), &numParenthesis); + + bcatcstr(metal, "~"); + TranslateOperandWithMaskMETAL(psContext, &psInst->asOperands[1], TO_FLAG_INTEGER, GetOperandWriteMaskMETAL(&psInst->asOperands[0])); + METALAddAssignPrologue(psContext, numParenthesis); + break; + } + case OPCODE_XOR: + { +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//XOR\n"); +#endif + + METALCallBinaryOp(psContext, "^", psInst, 0, 1, 2, SVT_UINT); + break; + } + case OPCODE_RESINFO: + { + uint32_t destElemCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + uint32_t destElem; +#ifdef _DEBUG + AddIndentation(psContext); + bcatcstr(metal, "//RESINFO\n"); +#endif + + for (destElem = 0; destElem < destElemCount; ++destElem) + { + + GetResInfoDataMETAL(psContext, psInst, psInst->asOperands[2].aui32Swizzle[destElem], destElem); + } + + break; + } + + + case OPCODE_DMAX: + case OPCODE_DMIN: + case OPCODE_DMUL: + case OPCODE_DEQ: + case OPCODE_DGE: + case OPCODE_DLT: + case OPCODE_DNE: + case OPCODE_DMOV: + case OPCODE_DMOVC: + case OPCODE_DTOF: + case OPCODE_FTOD: + case OPCODE_DDIV: + case OPCODE_DFMA: + case OPCODE_DRCP: + case OPCODE_MSAD: + case OPCODE_DTOI: + case OPCODE_DTOU: + case OPCODE_ITOD: + case OPCODE_UTOD: + default: + { + ASSERT(0); + break; + } + } + + if (psInst->bSaturate) //Saturate is only for floating point data (float opcodes or MOV) + { + int dstCount = GetNumSwizzleElementsMETAL(&psInst->asOperands[0]); + AddIndentation(psContext); + METALAddAssignToDest(psContext, &psInst->asOperands[0], SVT_FLOAT, dstCount, &numParenthesis); + bcatcstr(metal, "clamp("); + + TranslateOperandMETAL(psContext, &psInst->asOperands[0], TO_AUTO_BITCAST_TO_FLOAT); + bcatcstr(metal, ", 0.0, 1.0)"); + METALAddAssignPrologue(psContext, numParenthesis); + } +} + +static int METALIsIntegerImmediateOpcode(OPCODE_TYPE eOpcode) +{ + switch (eOpcode) + { + case OPCODE_IADD: + case OPCODE_IF: + case OPCODE_IEQ: + case OPCODE_IGE: + case OPCODE_ILT: + case OPCODE_IMAD: + case OPCODE_IMAX: + case OPCODE_IMIN: + case OPCODE_IMUL: + case OPCODE_INE: + case OPCODE_INEG: + case OPCODE_ISHL: + case OPCODE_ISHR: + case OPCODE_ITOF: + case OPCODE_USHR: + case OPCODE_AND: + case OPCODE_OR: + case OPCODE_XOR: + case OPCODE_BREAKC: + case OPCODE_CONTINUEC: + case OPCODE_RETC: + case OPCODE_DISCARD: + //MOV is typeless. + //Treat immediates as int, bitcast to float if necessary + case OPCODE_MOV: + case OPCODE_MOVC: + { + return 1; + } + default: + { + return 0; + } + } +} + +int InstructionUsesRegisterMETAL(const Instruction* psInst, const Operand* psOperand) +{ + uint32_t operand; + for (operand = 0; operand < psInst->ui32NumOperands; ++operand) + { + if (psInst->asOperands[operand].eType == psOperand->eType) + { + if (psInst->asOperands[operand].ui32RegisterNumber == psOperand->ui32RegisterNumber) + { + if (CompareOperandSwizzlesMETAL(&psInst->asOperands[operand], psOperand)) + { + return 1; + } + } + } + } + return 0; +} + +void MarkIntegerImmediatesMETAL(HLSLCrossCompilerContext* psContext) +{ + const uint32_t count = psContext->psShader->asPhase[MAIN_PHASE].pui32InstCount[0]; + Instruction* psInst = psContext->psShader->asPhase[MAIN_PHASE].ppsInst[0]; + uint32_t i; + + for (i = 0; i < count; ) + { + if (psInst[i].eOpcode == OPCODE_MOV && psInst[i].asOperands[1].eType == OPERAND_TYPE_IMMEDIATE32 && + psInst[i].asOperands[0].eType == OPERAND_TYPE_TEMP) + { + uint32_t k; + + for (k = i + 1; k < count; ++k) + { + if (psInst[k].eOpcode == OPCODE_ILT) + { + k = k; + } + if (InstructionUsesRegisterMETAL(&psInst[k], &psInst[i].asOperands[0])) + { + if (METALIsIntegerImmediateOpcode(psInst[k].eOpcode)) + { + psInst[i].asOperands[1].iIntegerImmediate = 1; + } + + goto next_iteration; + } + } + } +next_iteration: + ++i; + } +} diff --git a/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALOperand.c b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALOperand.c new file mode 100644 index 0000000000..f1ab027108 --- /dev/null +++ b/Code/Tools/HLSLCrossCompilerMETAL/src/toMETALOperand.c @@ -0,0 +1,2377 @@ +// Modifications copyright Amazon.com, Inc. or its affiliates +// Modifications copyright Crytek GmbH + +#include "internal_includes/toMETALOperand.h" +#include "internal_includes/toMETALDeclaration.h" +#include "bstrlib.h" +#include "hlslcc.h" +#include "internal_includes/debug.h" + +#include +#include + +#ifdef _MSC_VER +#define isnan(x) _isnan(x) +#define isinf(x) (!_finite(x)) +#endif + +#define fpcheck(x) (isnan(x) || isinf(x)) +#define MAX_STR_LENGTH 128 + +extern void AddIndentation(HLSLCrossCompilerContext* psContext); + +uint32_t SVTTypeToFlagMETAL(const SHADER_VARIABLE_TYPE eType) +{ + if (eType == SVT_UINT) + { + return TO_FLAG_UNSIGNED_INTEGER; + } + else if (eType == SVT_INT) + { + return TO_FLAG_INTEGER; + } + else if (eType == SVT_BOOL) + { + return TO_FLAG_INTEGER; // TODO bools? + } + else if (eType == SVT_FLOAT16) + { + return TO_FLAG_FLOAT16; + } + else + { + return TO_FLAG_NONE; + } +} + +SHADER_VARIABLE_TYPE TypeFlagsToSVTTypeMETAL(const uint32_t typeflags) +{ + if (typeflags & (TO_FLAG_INTEGER | TO_AUTO_BITCAST_TO_INT)) + { + return SVT_INT; + } + if (typeflags & (TO_FLAG_UNSIGNED_INTEGER | TO_AUTO_BITCAST_TO_UINT)) + { + return SVT_UINT; + } + if (typeflags & (TO_FLAG_FLOAT16 | TO_AUTO_BITCAST_TO_FLOAT16)) + { + return SVT_FLOAT16; + } + return SVT_FLOAT; +} + +uint32_t GetOperandWriteMaskMETAL(const Operand* psOperand) +{ + if (psOperand->eSelMode != OPERAND_4_COMPONENT_MASK_MODE || psOperand->ui32CompMask == 0) + { + return OPERAND_4_COMPONENT_MASK_ALL; + } + + return psOperand->ui32CompMask; +} + + +const char* GetConstructorForTypeMETAL(const SHADER_VARIABLE_TYPE eType, + const int components) +{ + static const char* const uintTypes[] = { " ", "uint", "uint2", "uint3", "uint4" }; + static const char* const intTypes[] = { " ", "int", "int2", "int3", "int4" }; + static const char* const floatTypes[] = { " ", "float", "float2", "float3", "float4" }; + static const char* const float16Types[] = { " ", "half", "half2", "half3", "half4" }; + + if (components < 1 || components > 4) + { + return "ERROR TOO MANY COMPONENTS IN VECTOR"; + } + + switch (eType) + { + case SVT_UINT: + return uintTypes[components]; + case SVT_INT: + return intTypes[components]; + case SVT_FLOAT: + return floatTypes[components]; + case SVT_FLOAT16: + return float16Types[components]; + default: + return "ERROR UNSUPPORTED TYPE"; + } +} + + +const char* GetConstructorForTypeFlagMETAL(const uint32_t ui32Flag, + const int components) +{ + if (ui32Flag & TO_FLAG_UNSIGNED_INTEGER || ui32Flag & TO_AUTO_BITCAST_TO_UINT) + { + return GetConstructorForTypeMETAL(SVT_UINT, components); + } + else if (ui32Flag & TO_FLAG_INTEGER || ui32Flag & TO_AUTO_BITCAST_TO_INT) + { + return GetConstructorForTypeMETAL(SVT_INT, components); + } + else + { + return GetConstructorForTypeMETAL(SVT_FLOAT, components); + } +} + +int GetMaxComponentFromComponentMaskMETAL(const Operand* psOperand) +{ + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + return 4; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + return 3; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + return 2; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + return 1; + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + return 4; + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + return 1; + } + } + + return 4; +} + +//Single component repeated +//e..g .wwww +uint32_t IsSwizzleReplicatedMETAL(const Operand* psOperand) +{ + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == WWWW_SWIZZLE || + psOperand->ui32Swizzle == ZZZZ_SWIZZLE || + psOperand->ui32Swizzle == YYYY_SWIZZLE || + psOperand->ui32Swizzle == XXXX_SWIZZLE) + { + return 1; + } + } + } + return 0; +} + +static uint32_t METALGetNumberBitsSet(uint32_t a) +{ + // Calculate number of bits in a + // Taken from https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64 + // Works only up to 14 bits (we're only using up to 4) + return (a * 0x200040008001ULL & 0x111111111111111ULL) % 0xf; +} + +//e.g. +//.z = 1 +//.x = 1 +//.yw = 2 +uint32_t GetNumSwizzleElementsMETAL(const Operand* psOperand) +{ + return GetNumSwizzleElementsWithMaskMETAL(psOperand, OPERAND_4_COMPONENT_MASK_ALL); +} + +// Get the number of elements returned by operand, taking additional component mask into account +uint32_t GetNumSwizzleElementsWithMaskMETAL(const Operand* psOperand, uint32_t ui32CompMask) +{ + uint32_t count = 0; + + switch (psOperand->eType) + { + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: + return 1; // TODO: does mask make any sense here? + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: + case OPERAND_TYPE_INPUT_THREAD_ID: + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: + // Adjust component count and break to more processing + ((Operand*)psOperand)->iNumComponents = 3; + break; + case OPERAND_TYPE_IMMEDIATE32: + case OPERAND_TYPE_IMMEDIATE64: + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + case OPERAND_TYPE_OUTPUT_DEPTH: + { + // Translate numComponents into bitmask + // 1 -> 1, 2 -> 3, 3 -> 7 and 4 -> 15 + uint32_t compMask = (1 << psOperand->iNumComponents) - 1; + + compMask &= ui32CompMask; + // Calculate bits left in compMask + return METALGetNumberBitsSet(compMask); + } + default: + { + break; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents != 1) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t compMask = psOperand->ui32CompMask; + if (compMask == 0) + { + compMask = OPERAND_4_COMPONENT_MASK_ALL; + } + compMask &= ui32CompMask; + + if (compMask == OPERAND_4_COMPONENT_MASK_ALL) + { + return 4; + } + + if (compMask & OPERAND_4_COMPONENT_MASK_X) + { + count++; + } + if (compMask & OPERAND_4_COMPONENT_MASK_Y) + { + count++; + } + if (compMask & OPERAND_4_COMPONENT_MASK_Z) + { + count++; + } + if (compMask & OPERAND_4_COMPONENT_MASK_W) + { + count++; + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if ((ui32CompMask & (1 << i)) == 0) + { + continue; + } + + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + count++; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + count++; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + count++; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + count++; + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + count++; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + count++; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + count++; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + count++; + } + } + + //Component Select 1 + } + + if (!count) + { + // Translate numComponents into bitmask + // 1 -> 1, 2 -> 3, 3 -> 7 and 4 -> 15 + uint32_t compMask = (1 << psOperand->iNumComponents) - 1; + + compMask &= ui32CompMask; + // Calculate bits left in compMask + return METALGetNumberBitsSet(compMask); + } + + return count; +} + +void AddSwizzleUsingElementCountMETAL(HLSLCrossCompilerContext* psContext, uint32_t count) +{ + bstring metal = *psContext->currentShaderString; + if (count == 4) + { + return; + } + if (count) + { + bcatcstr(metal, "."); + bcatcstr(metal, "x"); + count--; + } + if (count) + { + bcatcstr(metal, "y"); + count--; + } + if (count) + { + bcatcstr(metal, "z"); + count--; + } + if (count) + { + bcatcstr(metal, "w"); + count--; + } +} + +static uint32_t METALConvertOperandSwizzleToComponentMask(const Operand* psOperand) +{ + uint32_t mask = 0; + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + mask = psOperand->ui32CompMask; + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + mask |= OPERAND_4_COMPONENT_MASK_X; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + mask |= OPERAND_4_COMPONENT_MASK_Y; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + mask |= OPERAND_4_COMPONENT_MASK_Z; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + mask |= OPERAND_4_COMPONENT_MASK_W; + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + mask |= OPERAND_4_COMPONENT_MASK_X; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + mask |= OPERAND_4_COMPONENT_MASK_Y; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + mask |= OPERAND_4_COMPONENT_MASK_Z; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + mask |= OPERAND_4_COMPONENT_MASK_W; + } + } + + //Component Select 1 + } + + return mask; +} + +//Non-zero means the components overlap +int CompareOperandSwizzlesMETAL(const Operand* psOperandA, const Operand* psOperandB) +{ + uint32_t maskA = METALConvertOperandSwizzleToComponentMask(psOperandA); + uint32_t maskB = METALConvertOperandSwizzleToComponentMask(psOperandB); + + return maskA & maskB; +} + + +void TranslateOperandSwizzleMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + TranslateOperandSwizzleWithMaskMETAL(psContext, psOperand, OPERAND_4_COMPONENT_MASK_ALL); +} + +void TranslateOperandSwizzleWithMaskMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask) +{ + bstring metal = *psContext->currentShaderString; + + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return; + } + } + + if (psOperand->eType == OPERAND_TYPE_CONSTANT_BUFFER) + { + /*ConstantBuffer* psCBuf = NULL; + ShaderVar* psVar = NULL; + int32_t index = -1; + GetConstantBufferFromBindingPoint(psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + + //Access the Nth vec4 (N=psOperand->aui32ArraySizes[1]) + //then apply the sizzle. + + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVar, &index); + + bformata(metal, ".%s", psVar->Name); + if(index != -1) + { + bformata(metal, "[%d]", index); + }*/ + + //return; + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents != 1) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t mask; + if (psOperand->ui32CompMask != 0) + { + mask = psOperand->ui32CompMask & ui32ComponentMask; + } + else + { + mask = ui32ComponentMask; + } + + if (mask != 0 && mask != OPERAND_4_COMPONENT_MASK_ALL) + { + bcatcstr(metal, "."); + if (mask & OPERAND_4_COMPONENT_MASK_X) + { + bcatcstr(metal, "x"); + } + if (mask & OPERAND_4_COMPONENT_MASK_Y) + { + bcatcstr(metal, "y"); + } + if (mask & OPERAND_4_COMPONENT_MASK_Z) + { + bcatcstr(metal, "z"); + } + if (mask & OPERAND_4_COMPONENT_MASK_W) + { + bcatcstr(metal, "w"); + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (ui32ComponentMask != OPERAND_4_COMPONENT_MASK_ALL || + !(psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X && + psOperand->aui32Swizzle[1] == OPERAND_4_COMPONENT_Y && + psOperand->aui32Swizzle[2] == OPERAND_4_COMPONENT_Z && + psOperand->aui32Swizzle[3] == OPERAND_4_COMPONENT_W + ) + ) + { + uint32_t i; + + bcatcstr(metal, "."); + + for (i = 0; i < 4; ++i) + { + if (!(ui32ComponentMask & (OPERAND_4_COMPONENT_MASK_X << i))) + { + continue; + } + + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + bcatcstr(metal, "x"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + bcatcstr(metal, "y"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + bcatcstr(metal, "z"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + bcatcstr(metal, "w"); + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) // ui32ComponentMask is ignored in this case + { + bcatcstr(metal, "."); + + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + bcatcstr(metal, "x"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + bcatcstr(metal, "y"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + bcatcstr(metal, "z"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + bcatcstr(metal, "w"); + } + } + + //Component Select 1 + } +} + +void TranslateGmemOperandSwizzleWithMaskMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32ComponentMask, uint32_t gmemNumElements) +{ + // Similar as TranslateOperandSwizzleWithMaskMETAL but need to considerate max # of elements + + bstring metal = *psContext->currentShaderString; + + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents != 1) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t mask; + if (psOperand->ui32CompMask != 0) + { + mask = psOperand->ui32CompMask & ui32ComponentMask; + } + else + { + mask = ui32ComponentMask; + } + + if (mask != 0 && mask != OPERAND_4_COMPONENT_MASK_ALL) + { + bcatcstr(metal, "."); + if (mask & OPERAND_4_COMPONENT_MASK_X) + { + bcatcstr(metal, "x"); + } + if (mask & OPERAND_4_COMPONENT_MASK_Y) + { + if (gmemNumElements < 2) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "y"); + } + } + if (mask & OPERAND_4_COMPONENT_MASK_Z) + { + if (gmemNumElements < 3) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "z"); + } + } + if (mask & OPERAND_4_COMPONENT_MASK_W) + { + if (gmemNumElements < 4) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "w"); + } + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (ui32ComponentMask != OPERAND_4_COMPONENT_MASK_ALL || + !(psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X && + psOperand->aui32Swizzle[1] == OPERAND_4_COMPONENT_Y && + psOperand->aui32Swizzle[2] == OPERAND_4_COMPONENT_Z && + psOperand->aui32Swizzle[3] == OPERAND_4_COMPONENT_W + ) + ) + { + uint32_t i; + + bcatcstr(metal, "."); + + for (i = 0; i < 4; ++i) + { + if (!(ui32ComponentMask & (OPERAND_4_COMPONENT_MASK_X << i))) + { + continue; + } + + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + bcatcstr(metal, "x"); + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + if (gmemNumElements < 2) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "y"); + } + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + if (gmemNumElements < 3) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "z"); + } + } + else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + if (gmemNumElements < 4) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "w"); + } + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) // ui32ComponentMask is ignored in this case + { + bcatcstr(metal, "."); + + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + bcatcstr(metal, "x"); + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + if (gmemNumElements < 2) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "y"); + } + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + if (gmemNumElements < 3) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "z"); + } + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + if (gmemNumElements < 4) + { + bcatcstr(metal, "x"); + } + else + { + bcatcstr(metal, "w"); + } + } + } + + //Component Select 1 + } +} + +int GetFirstOperandSwizzleMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + if (psOperand->eType == OPERAND_TYPE_INPUT) + { + if (psContext->psShader->abScalarInput[psOperand->ui32RegisterNumber]) + { + return -1; + } + } + + if (psOperand->iWriteMaskEnabled && + psOperand->iNumComponents == 4) + { + //Component Mask + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + if (psOperand->ui32CompMask != 0 && psOperand->ui32CompMask != (OPERAND_4_COMPONENT_MASK_X | OPERAND_4_COMPONENT_MASK_Y | OPERAND_4_COMPONENT_MASK_Z | OPERAND_4_COMPONENT_MASK_W)) + { + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_X) + { + return 0; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Y) + { + return 1; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_Z) + { + return 2; + } + if (psOperand->ui32CompMask & OPERAND_4_COMPONENT_MASK_W) + { + return 3; + } + } + } + else + //Component Swizzle + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle != (NO_SWIZZLE)) + { + uint32_t i; + + for (i = 0; i < 4; ++i) + { + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) + { + return 0; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) + { + return 1; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) + { + return 2; + } + else + if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) + { + return 3; + } + } + } + } + else + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) + { + return 0; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) + { + return 1; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) + { + return 2; + } + else + if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) + { + return 3; + } + } + + //Component Select 1 + } + + return -1; +} + +void TranslateOperandIndexMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index) +{ + int i = index; + + bstring metal = *psContext->currentShaderString; + + ASSERT(index < psOperand->iIndexDims); + + switch (psOperand->eIndexRep[i]) + { + case OPERAND_INDEX_IMMEDIATE32: + { + if (i > 0) + { + bformata(metal, "[%d]", psOperand->aui32ArraySizes[i]); + } + else + { + bformata(metal, "%d", psOperand->aui32ArraySizes[i]); + } + break; + } + case OPERAND_INDEX_RELATIVE: + { + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bcatcstr(metal, "]"); + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + bcatcstr(metal, "["); //Indexes must be integral. + TranslateOperandMETAL(psContext, psOperand->psSubOperand[i], TO_FLAG_INTEGER); + bformata(metal, " + %d]", psOperand->aui32ArraySizes[i]); + break; + } + default: + { + break; + } + } +} + +void TranslateOperandIndexMADMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, int index, uint32_t multiply, uint32_t add) +{ + int i = index; + + bstring metal = *psContext->currentShaderString; + + ASSERT(index < psOperand->iIndexDims); + + switch (psOperand->eIndexRep[i]) + { + case OPERAND_INDEX_IMMEDIATE32: + { + if (i > 0) + { + bformata(metal, "[%d*%d+%d]", psOperand->aui32ArraySizes[i], multiply, add); + } + else + { + bformata(metal, "%d*%d+%d", psOperand->aui32ArraySizes[i], multiply, add); + } + break; + } + case OPERAND_INDEX_RELATIVE: + { + bcatcstr(metal, "[int("); //Indexes must be integral. + TranslateOperandMETAL(psContext, psOperand->psSubOperand[i], TO_FLAG_NONE); + bformata(metal, ")*%d+%d]", multiply, add); + break; + } + case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: + { + bcatcstr(metal, "[(int("); //Indexes must be integral. + TranslateOperandMETAL(psContext, psOperand->psSubOperand[i], TO_FLAG_NONE); + bformata(metal, ") + %d)*%d+%d]", psOperand->aui32ArraySizes[i], multiply, add); + break; + } + default: + { + break; + } + } +} + +// Returns nonzero if a direct constructor can convert src->dest +static int METALCanDoDirectCast( SHADER_VARIABLE_TYPE src, SHADER_VARIABLE_TYPE dest) +{ + // uint<->int<->bool conversions possible + if ((src == SVT_INT || src == SVT_UINT || src == SVT_BOOL) && (dest == SVT_INT || dest == SVT_UINT || dest == SVT_BOOL)) + { + return 1; + } + + // float<->double possible + if ((src == SVT_FLOAT || src == SVT_DOUBLE) && (dest == SVT_FLOAT || dest == SVT_DOUBLE)) + { + return 1; + } + + return 0; +} + +// Returns true if one of the src or dest is half float while the other is not +static int IsHalfFloatCastNeeded(SHADER_VARIABLE_TYPE src, SHADER_VARIABLE_TYPE dest) +{ + // uint<->int<->bool conversions possible + if ((src == SVT_FLOAT16) && (dest != SVT_FLOAT16)) + { + return 1; + } + + // float<->double possible + if ((src != SVT_FLOAT16) && (dest == SVT_FLOAT16)) + { + return 1; + } + + return 0; +} + +static const char* GetOpDestType(SHADER_VARIABLE_TYPE to) +{ + switch (to) + { + case SVT_FLOAT: + return "float"; + break; + case SVT_FLOAT16: + return "half"; + break; + case SVT_INT: + return "int"; + break; + case SVT_UINT: + return "uint"; + break; + default: + ASSERT(0); + return ""; + } +} + +static const char* GetOpCastType(SHADER_VARIABLE_TYPE from, SHADER_VARIABLE_TYPE to) +{ + if (to == SVT_FLOAT && (from == SVT_INT || from == SVT_UINT)) + { + return "as_type"; + } + else if (to == SVT_INT && (from == SVT_FLOAT || from == SVT_UINT)) + { + return "as_type"; + } + else if (to == SVT_UINT && (from == SVT_FLOAT || from == SVT_INT)) + { + return "as_type"; + } + + ASSERT(0); + return "ERROR missing components in GetBitcastOp()"; +} + +// Helper function to print out a single 32-bit immediate value in desired format +static void METALprintImmediate32(HLSLCrossCompilerContext* psContext, uint32_t value, SHADER_VARIABLE_TYPE eType) +{ + bstring metal = *psContext->currentShaderString; + int needsParenthesis = 0; + + if (eType == SVT_FLOAT || eType == SVT_FLOAT16) + { + // Print floats as bit patterns. + switch (eType) + { + case SVT_FLOAT: + bcatcstr(metal, "as_type("); + break; + case SVT_FLOAT16: + bcatcstr(metal, "static_cast("); + break; + } + + eType = SVT_INT; + needsParenthesis = 1; + } + + + + switch (eType) + { + default: + case SVT_INT: + // Need special handling for anything >= uint 0x3fffffff + if (value > 0x3ffffffe) + { + bformata(metal, "int(0x%Xu)", value); + } + else + { + bformata(metal, "0x%X", value); + } + break; + case SVT_UINT: + bformata(metal, "%uu", value); + break; + case SVT_FLOAT: + bformata(metal, "%f", *((float*)(&value))); + break; + } + if (needsParenthesis) + { + bcatcstr(metal, ")"); + } +} + +static void METALMETALTranslateVariableNameWithMask(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle, uint32_t ui32CompMask) +{ + int numParenthesis = 0; + int hasCtor = 0; + bstring metal = *psContext->currentShaderString; + SHADER_VARIABLE_TYPE requestedType = TypeFlagsToSVTTypeMETAL(ui32TOFlag); + SHADER_VARIABLE_TYPE eType = GetOperandDataTypeExMETAL(psContext, psOperand, requestedType); + int numComponents = GetNumSwizzleElementsWithMaskMETAL(psOperand, ui32CompMask); + int requestedComponents = 0; + + if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC2) + { + requestedComponents = 2; + } + else if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC3) + { + requestedComponents = 3; + } + else if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC4) + { + requestedComponents = 4; + } + + requestedComponents = max(requestedComponents, numComponents); + + *pui32IgnoreSwizzle = 0; + + + if (!(ui32TOFlag & (TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY | TO_FLAG_DECLARATION_NAME))) + { + if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32 || psOperand->eType == OPERAND_TYPE_IMMEDIATE64) + { + // Mark the operand type to match whatever we're asking for in the flags. + ((Operand*)psOperand)->aeDataType[0] = requestedType; + ((Operand*)psOperand)->aeDataType[1] = requestedType; + ((Operand*)psOperand)->aeDataType[2] = requestedType; + ((Operand*)psOperand)->aeDataType[3] = requestedType; + } + + if (eType != requestedType) + { + if (METALCanDoDirectCast(eType, requestedType)) + { + bformata(metal, "%s(", GetConstructorForTypeMETAL(requestedType, requestedComponents)); + hasCtor = 1; + } + else if (IsHalfFloatCastNeeded(eType, requestedType)) + { + // half float static cast needed + if (requestedComponents > 1) + { + bformata(metal, "static_cast<%s%i>(", GetOpDestType(requestedType), requestedComponents); + } + else + { + bformata(metal, "static_cast<%s>(", GetOpDestType(requestedType)); + } + } + else + { + // Direct cast not possible, need to do bitcast. + if (requestedComponents > 1) + { + bformata(metal, "%s<%s%i>(", GetOpCastType(eType, requestedType), GetOpDestType(requestedType), requestedComponents); + } + else + { + bformata(metal, "%s<%s>(", GetOpCastType(eType, requestedType), GetOpDestType(requestedType)); + } + } + numParenthesis++; + } + + // Add ctor if needed (upscaling) + if (numComponents < requestedComponents && (hasCtor == 0)) + { + ASSERT(numComponents == 1); + bformata(metal, "%s(", GetConstructorForTypeMETAL(requestedType, requestedComponents)); + numParenthesis++; + hasCtor = 1; + } + } + + + switch (psOperand->eType) + { + case OPERAND_TYPE_IMMEDIATE32: + { + if (psOperand->iNumComponents == 1) + { + METALprintImmediate32(psContext, *((unsigned int*)(&psOperand->afImmediates[0])), requestedType); + } + else + { + int i; + int firstItemAdded = 0; + if (hasCtor == 0) + { + bformata(metal, "%s(", GetConstructorForTypeMETAL(requestedType, numComponents)); + numParenthesis++; + hasCtor = 1; + } + for (i = 0; i < 4; i++) + { + uint32_t uval; + if (!(ui32CompMask & (1 << i))) + { + continue; + } + + if (firstItemAdded) + { + bcatcstr(metal, ", "); + } + uval = *((uint32_t*)(&psOperand->afImmediates[i])); + METALprintImmediate32(psContext, uval, requestedType); + firstItemAdded = 1; + } + bcatcstr(metal, ")"); + *pui32IgnoreSwizzle = 1; + numParenthesis--; + } + break; + } + case OPERAND_TYPE_IMMEDIATE64: + { + if (psOperand->iNumComponents == 1) + { + bformata(metal, "%f", + psOperand->adImmediates[0]); + } + else + { + bformata(metal, "float4(%f, %f, %f, %f)", + psOperand->adImmediates[0], + psOperand->adImmediates[1], + psOperand->adImmediates[2], + psOperand->adImmediates[3]); + if (psOperand->iNumComponents != 4) + { + AddSwizzleUsingElementCountMETAL(psContext, psOperand->iNumComponents); + } + } + break; + } + case OPERAND_TYPE_INPUT: + { + switch (psOperand->iIndexDims) + { + case INDEX_2D: + { + if (psOperand->aui32ArraySizes[1] == 0) //Input index zero - position. + { + bcatcstr(metal, "stageIn"); + TranslateOperandIndexMETAL(psContext, psOperand, 0); //Vertex index + bcatcstr(metal, ".position"); + } + else + { + const char* name = "Input"; + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + name = GetDeclaredInputNameMETAL(psContext, psContext->psShader->eShaderType, psOperand); + } + + bformata(metal, "%s%d", name, psOperand->aui32ArraySizes[1]); + TranslateOperandIndexMETAL(psContext, psOperand, 0); //Vertex index + } + break; + } + default: + { + if (psOperand->eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE) + { + bformata(metal, "Input%d[", psOperand->ui32RegisterNumber); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(metal, "]"); + } + else + { + if (psContext->psShader->aIndexedInput[psOperand->ui32RegisterNumber] != 0) + { + const uint32_t parentIndex = psContext->psShader->aIndexedInputParents[psOperand->ui32RegisterNumber]; + bformata(metal, "Input%d[%d]", parentIndex, + psOperand->ui32RegisterNumber - parentIndex); + } + else + { + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + const char* name = GetDeclaredInputNameMETAL(psContext, psContext->psShader->eShaderType, psOperand); + bcatcstr(metal, name); + } + else + { + bformata(metal, "Input%d", psOperand->ui32RegisterNumber); + } + } + } + break; + } + } + break; + } + case OPERAND_TYPE_OUTPUT: + { + bformata(metal, "Output%d", psOperand->ui32RegisterNumber); + if (psOperand->psSubOperand[0]) + { + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[0], TO_AUTO_BITCAST_TO_INT); + bcatcstr(metal, "]"); + } + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH: + { + bcatcstr(metal, "DepthAny"); + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: + { + bcatcstr(metal, "DepthGreater"); + break; + } + case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: + { + bcatcstr(metal, "DepthLess"); + break; + } + case OPERAND_TYPE_TEMP: + { + SHADER_VARIABLE_TYPE eType2 = GetOperandDataTypeMETAL(psContext, psOperand); + bcatcstr(metal, "Temp"); + + if (eType2 == SVT_INT) + { + bcatcstr(metal, "_int"); + } + else if (eType2 == SVT_UINT) + { + bcatcstr(metal, "_uint"); + } + else if (eType2 == SVT_DOUBLE) + { + bcatcstr(metal, "_double"); + } + else if (eType2 == SVT_FLOAT16) + { + bcatcstr(metal, "_half"); + } + else if (eType2 == SVT_VOID && + (ui32TOFlag & TO_FLAG_DESTINATION)) + { + ASSERT(0 && "Should never get here!"); + /* if(ui32TOFlag & TO_FLAG_INTEGER) + { + bcatcstr(metal, "_int"); + } + else + if(ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) + { + bcatcstr(metal, "_uint"); + }*/ + } + + bformata(metal, "[%d]", psOperand->ui32RegisterNumber); + + break; + } + case OPERAND_TYPE_SPECIAL_IMMCONSTINT: + { + bformata(metal, "IntImmConst%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_SPECIAL_IMMCONST: + { + if (psOperand->psSubOperand[0] != NULL) + { + if (psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber] != 0) + { + bformata(metal, "ImmConstArray[%d + ", psContext->psShader->aui32Dx9ImmConstArrayRemap[psOperand->ui32RegisterNumber]); + } + else + { + bcatcstr(metal, "ImmConstArray["); + } + TranslateOperandWithMaskMETAL(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER, OPERAND_4_COMPONENT_MASK_X); + bcatcstr(metal, "]"); + } + else + { + bformata(metal, "ImmConst%d", psOperand->ui32RegisterNumber); + } + break; + } + case OPERAND_TYPE_SPECIAL_OUTBASECOLOUR: + { + bcatcstr(metal, "BaseColour"); + break; + } + case OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR: + { + bcatcstr(metal, "OffsetColour"); + break; + } + case OPERAND_TYPE_SPECIAL_POSITION: + { + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "stageIn."); + } + bcatcstr(metal, "position"); + break; + } + case VERTEX_SHADER: + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "output."); + } + bcatcstr(metal, "position"); + break; + } + default: + { + break; + } + } + break; + } + case OPERAND_TYPE_SPECIAL_FOG: + { + bcatcstr(metal, "Fog"); + break; + } + case OPERAND_TYPE_SPECIAL_POINTSIZE: + { + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "stageIn."); + } + bcatcstr(metal, "pointSize"); + break; + } + case VERTEX_SHADER: + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "output."); + } + bcatcstr(metal, "pointSize"); + break; + } + default: + { + break; + } + } + break; + } + case OPERAND_TYPE_SPECIAL_ADDRESS: + { + bcatcstr(metal, "Address"); + break; + } + case OPERAND_TYPE_SPECIAL_LOOPCOUNTER: + { + bcatcstr(metal, "LoopCounter"); + pui32IgnoreSwizzle[0] = 1; + break; + } + case OPERAND_TYPE_SPECIAL_TEXCOORD: + { + bformata(metal, "TexCoord%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_CONSTANT_BUFFER: + { + const char* StageName = "VS"; + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + + switch (psContext->psShader->eShaderType) + { + case PIXEL_SHADER: + { + StageName = "PS"; + break; + } + ////////////////////// FOLLOWING SHOULDN'T HIT IN METAL AS IT'S NOT SUPPORTED ////////////////////////////////////////// + case HULL_SHADER: + { + StageName = "HS"; + break; + } + case DOMAIN_SHADER: + { + StageName = "DS"; + break; + } + case GEOMETRY_SHADER: + { + StageName = "GS"; + break; + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + case COMPUTE_SHADER: + { + StageName = "CS"; + break; + } + default: + { + break; + } + } + + if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) + { + pui32IgnoreSwizzle[0] = 1; + } + + // FIXME: With ES 3.0 the buffer name is often not prepended to variable names + if (((psContext->flags & HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) != HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT) && + ((psContext->flags & HLSLCC_FLAG_DISABLE_GLOBALS_STRUCT) != HLSLCC_FLAG_DISABLE_GLOBALS_STRUCT)) + { + if (psCBuf) + { + //$Globals. + if (psCBuf->Name[0] == '$') + { + bformata(metal, "Globals%s", StageName); + } + else + { + bformata(metal, "%s%s", psCBuf->Name, StageName); + } + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "."); + } + } + else + { + //bformata(metal, "cb%d", psOperand->aui32ArraySizes[0]); + } + } + + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + //Work out the variable name. Don't apply swizzle to that variable yet. + int32_t rebase = 0; + + if (psCBuf && !psCBuf->blob) + { + GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + + bformata(metal, "%s", psVarType->FullName); + } + else if (psCBuf) + { + bformata(metal, "%s%s_data", psCBuf->Name, StageName); + if (psContext->psShader->eShaderType == PIXEL_SHADER) + { + bformata(metal, ".%s", psCBuf->asVars->Name); + } + else if (psContext->psShader->eShaderType == VERTEX_SHADER) + { + bformata(metal, "->%s", psCBuf->asVars->Name); + } + else + { + ASSERT(0); + } + index = psOperand->aui32ArraySizes[1]; + } + else // We don't have a semantic for this variable, so try the raw dump appoach. + { + bformata(metal, "cb%d.data", psOperand->aui32ArraySizes[0]); // + index = psOperand->aui32ArraySizes[1]; + } + + //Dx9 only? + if (psOperand->psSubOperand[0] != NULL) + { + // Array of matrices is treated as array of vec4s in HLSL, + // but that would mess up uniform types in metal. Do gymnastics. + uint32_t opFlags = TO_FLAG_INTEGER; + + if (psVarType && (psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) && (psVarType->Elements > 1)) + { + // Special handling for matrix arrays + bcatcstr(metal, "[("); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[0], opFlags); + bformata(metal, ") / 4]"); + if (psContext->psShader->eTargetLanguage <= LANG_120) + { + bcatcstr(metal, "[int(mod(float("); + TranslateOperandWithMaskMETAL(psContext, psOperand->psSubOperand[0], opFlags, OPERAND_4_COMPONENT_MASK_X); + bformata(metal, "), 4.0))]"); + } + else + { + bcatcstr(metal, "[(("); + TranslateOperandWithMaskMETAL(psContext, psOperand->psSubOperand[0], opFlags, OPERAND_4_COMPONENT_MASK_X); + bformata(metal, ") %% 4)]"); + } + } + else + { + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[0], opFlags); + bformata(metal, "]"); + } + } + else + if (index != -1 && psOperand->psSubOperand[1] != NULL) + { + // Array of matrices is treated as array of vec4s in HLSL, + // but that would mess up uniform types in metal. Do gymnastics. + SHADER_VARIABLE_TYPE eType2 = GetOperandDataTypeMETAL(psContext, psOperand->psSubOperand[1]); + uint32_t opFlags = TO_FLAG_INTEGER; + if (eType2 != SVT_INT && eType2 != SVT_UINT) + { + opFlags = TO_AUTO_BITCAST_TO_INT; + } + + if (psVarType && (psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) && (psVarType->Elements > 1)) + { + // Special handling for matrix arrays + bcatcstr(metal, "[("); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[1], opFlags); + bformata(metal, " + %d) / 4]", index); + if (psContext->psShader->eTargetLanguage <= LANG_120) + { + bcatcstr(metal, "[int(mod(float("); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[1], opFlags); + bformata(metal, " + %d), 4.0))]", index); + } + else + { + bcatcstr(metal, "[(("); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[1], opFlags); + bformata(metal, " + %d) %% 4)]", index); + } + } + else + { + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[1], opFlags); + bformata(metal, " + %d]", index); + } + } + else if (index != -1) + { + if (psVarType && (psVarType->Class == SVC_MATRIX_COLUMNS || psVarType->Class == SVC_MATRIX_ROWS) && (psVarType->Elements > 1)) + { + // Special handling for matrix arrays, open them up into vec4's + size_t matidx = index / 4; + size_t rowidx = index - (matidx * 4); + bformata(metal, "[%d][%d]", matidx, rowidx); + } + else + { + bformata(metal, "[%d]", index); + } + } + else if (psOperand->psSubOperand[1] != NULL) + { + bcatcstr(metal, "["); + TranslateOperandMETAL(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + bcatcstr(metal, "]"); + } + + if (psVarType && psVarType->Class == SVC_VECTOR) + { + switch (rebase) + { + case 4: + { + if (psVarType->Columns == 2) + { + //.x(metal) is .y(HLSL). .y(metal) is .z(HLSL) + bcatcstr(metal, ".xxyx"); + } + else if (psVarType->Columns == 3) + { + //.x(metal) is .y(HLSL). .y(metal) is .z(HLSL) .z(metal) is .w(HLSL) + bcatcstr(metal, ".xxyz"); + } + break; + } + case 8: + { + if (psVarType->Columns == 2) + { + //.x(metal) is .z(HLSL). .y(metal) is .w(HLSL) + bcatcstr(metal, ".xxxy"); + } + break; + } + case 0: + default: + { + //No rebase, but extend to vec4. + if (psVarType->Columns == 2) + { + bcatcstr(metal, ".xyxx"); + } + else if (psVarType->Columns == 3) + { + bcatcstr(metal, ".xyzx"); + } + break; + } + } + } + + if (psVarType && psVarType->Class == SVC_SCALAR) + { + *pui32IgnoreSwizzle = 1; + } + } + break; + } + case OPERAND_TYPE_RESOURCE: + { + ResourceNameMETAL(metal, psContext, RGROUP_TEXTURE, psOperand->ui32RegisterNumber, 0); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_SAMPLER: + { + bformata(metal, "Sampler%d", psOperand->ui32RegisterNumber); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_FUNCTION_BODY: + { + const uint32_t ui32FuncBody = psOperand->ui32RegisterNumber; + const uint32_t ui32FuncTable = psContext->psShader->aui32FuncBodyToFuncTable[ui32FuncBody]; + //const uint32_t ui32FuncPointer = psContext->psShader->aui32FuncTableToFuncPointer[ui32FuncTable]; + const uint32_t ui32ClassType = psContext->psShader->sInfo.aui32TableIDToTypeID[ui32FuncTable]; + const char* ClassTypeName = &psContext->psShader->sInfo.psClassTypes[ui32ClassType].Name[0]; + const uint32_t ui32UniqueClassFuncIndex = psContext->psShader->ui32NextClassFuncName[ui32ClassType]++; + + bformata(metal, "%s_Func%d", ClassTypeName, ui32UniqueClassFuncIndex); + break; + } + case OPERAND_TYPE_INPUT_FORK_INSTANCE_ID: + { + bcatcstr(metal, "forkInstanceID"); + *pui32IgnoreSwizzle = 1; + return; + } + case OPERAND_TYPE_IMMEDIATE_CONSTANT_BUFFER: + { + bcatcstr(metal, "immediateConstBufferF"); + + if (psOperand->psSubOperand[0]) + { + bcatcstr(metal, "("); //Indexes must be integral. + TranslateOperandMETAL(psContext, psOperand->psSubOperand[0], TO_FLAG_INTEGER); + bcatcstr(metal, ")"); + } + break; + } + case OPERAND_TYPE_INPUT_DOMAIN_POINT: + { + bcatcstr(metal, "gl_TessCoord"); + break; + } + case OPERAND_TYPE_INPUT_CONTROL_POINT: + { + if (psOperand->aui32ArraySizes[1] == 0) //Input index zero - position. + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "stageIn."); + } + bformata(metal, "position", psOperand->aui32ArraySizes[0]); + } + else + { + bformata(metal, "Input%d[%d]", psOperand->aui32ArraySizes[1], psOperand->aui32ArraySizes[0]); + } + break; + } + case OPERAND_TYPE_NULL: + { + // Null register, used to discard results of operations + bcatcstr(metal, "//null"); + break; + } + case OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID: + { + break; + } + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "output."); + } + bcatcstr(metal, "sampleMask"); + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_COVERAGE_MASK: + { + if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) + { + bcatcstr(metal, "stageIn."); + } + bcatcstr(metal, "sampleMask"); + //Skip swizzle on scalar types. + *pui32IgnoreSwizzle = 1; + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID: //SV_DispatchThreadID + { + bcatcstr(metal, "vThreadID"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: //SV_GroupThreadID + { + bcatcstr(metal, "vThreadGroupID"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: //SV_GroupID + { + bcatcstr(metal, "vThreadIDInGroup"); + break; + } + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: //SV_GroupIndex + { + bcatcstr(metal, "vThreadIDInGroupFlattened"); + *pui32IgnoreSwizzle = 1; // No swizzle meaningful for scalar. + break; + } + case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: + { + ResourceNameMETAL(metal, psContext, RGROUP_UAV, psOperand->ui32RegisterNumber, 0); + if (ui32TOFlag | TO_FLAG_NAME_ONLY) + { + *pui32IgnoreSwizzle = 1; + } + break; + } + case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: + { + bformata(metal, "TGSM%d", psOperand->ui32RegisterNumber); + *pui32IgnoreSwizzle = 1; // No swizzle meaningful for scalar. + break; + } + case OPERAND_TYPE_INPUT_PRIMITIVEID: + { + break; + } + case OPERAND_TYPE_INDEXABLE_TEMP: + { + bformata(metal, "TempArray%d", psOperand->aui32ArraySizes[0]); + bcatcstr(metal, "["); + if (psOperand->aui32ArraySizes[1] != 0 || !psOperand->psSubOperand[1]) + { + bformata(metal, "%d", psOperand->aui32ArraySizes[1]); + } + + if (psOperand->psSubOperand[1]) + { + if (psOperand->aui32ArraySizes[1] != 0) + { + bcatcstr(metal, "+"); + } + TranslateOperandMETAL(psContext, psOperand->psSubOperand[1], TO_FLAG_INTEGER); + } + bcatcstr(metal, "]"); + break; + } + case OPERAND_TYPE_STREAM: + { + bformata(metal, "%d", psOperand->ui32RegisterNumber); + break; + } + case OPERAND_TYPE_INPUT_GS_INSTANCE_ID: + { + // No GS in METAL + break; + } + case OPERAND_TYPE_THIS_POINTER: + { + /* + The "this" register is a register that provides up to 4 pieces of information: + X: Which CB holds the instance data + Y: Base element offset of the instance data within the instance CB + Z: Base sampler index + W: Base Texture index + + Can be different for each function call + */ + break; + } + case OPERAND_TYPE_INPUT_PATCH_CONSTANT: + { + bformata(metal, "myPatchConst%d", psOperand->ui32RegisterNumber); + break; + } + default: + { + ASSERT(0); + break; + } + } + + if (hasCtor && (*pui32IgnoreSwizzle == 0)) + { + TranslateOperandSwizzleWithMaskMETAL(psContext, psOperand, ui32CompMask); + *pui32IgnoreSwizzle = 1; + } + + if (*pui32IgnoreSwizzle == 0) + { + TranslateOperandSwizzleWithMaskMETAL(psContext, psOperand, ui32CompMask); + } + + while (numParenthesis != 0) + { + bcatcstr(metal, ")"); + numParenthesis--; + } +} + +static void METALTranslateVariableName(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle) +{ + METALMETALTranslateVariableNameWithMask(psContext, psOperand, ui32TOFlag, pui32IgnoreSwizzle, OPERAND_4_COMPONENT_MASK_ALL); +} + + +SHADER_VARIABLE_TYPE GetOperandDataTypeMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand) +{ + return GetOperandDataTypeExMETAL(psContext, psOperand, SVT_INT); +} + +SHADER_VARIABLE_TYPE GetOperandDataTypeExMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, SHADER_VARIABLE_TYPE ePreferredTypeForImmediates) +{ + + // The min precision qualifier overrides all of the stuff below + if (psOperand->eMinPrecision == OPERAND_MIN_PRECISION_FLOAT_16) + { + return SVT_FLOAT16; + } + + switch (psOperand->eType) + { + case OPERAND_TYPE_TEMP: + { + SHADER_VARIABLE_TYPE eCurrentType = SVT_VOID; + int i = 0; + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) + { + return psOperand->aeDataType[psOperand->aui32Swizzle[0]]; + } + if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) + { + if (psOperand->ui32Swizzle == (NO_SWIZZLE)) + { + return psOperand->aeDataType[0]; + } + + return psOperand->aeDataType[psOperand->aui32Swizzle[0]]; + } + + if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) + { + uint32_t ui32CompMask = psOperand->ui32CompMask; + if (!psOperand->ui32CompMask) + { + ui32CompMask = OPERAND_4_COMPONENT_MASK_ALL; + } + for (; i < 4; ++i) + { + if (ui32CompMask & (1 << i)) + { + eCurrentType = psOperand->aeDataType[i]; + break; + } + } + + #ifdef _DEBUG + //Check if all elements have the same basic type. + for (; i < 4; ++i) + { + if (psOperand->ui32CompMask & (1 << i)) + { + if (eCurrentType != psOperand->aeDataType[i]) + { + ASSERT(0); + } + } + } + #endif + return eCurrentType; + } + + ASSERT(0); + + break; + } + case OPERAND_TYPE_OUTPUT: + { + const uint32_t ui32Register = psOperand->aui32ArraySizes[psOperand->iIndexDims - 1]; + InOutSignature* psOut; + + if (GetOutputSignatureFromRegister(psContext->currentPhase, + ui32Register, + psOperand->ui32CompMask, + 0, + &psContext->psShader->sInfo, + &psOut)) + { + if (psOut->eComponentType == INOUT_COMPONENT_UINT32) + { + return SVT_UINT; + } + else if (psOut->eComponentType == INOUT_COMPONENT_SINT32) + { + return SVT_INT; + } + } + break; + } + case OPERAND_TYPE_INPUT: + { + const uint32_t ui32Register = psOperand->aui32ArraySizes[psOperand->iIndexDims - 1]; + InOutSignature* psIn; + + //UINT in DX, INT in GL. + if (psOperand->eSpecialName == NAME_PRIMITIVE_ID) + { + return SVT_INT; + } + if (psOperand->eSpecialName == NAME_IS_FRONT_FACE) + { + return SVT_BOOL; + } + + if (GetInputSignatureFromRegister(ui32Register, &psContext->psShader->sInfo, &psIn)) + { + if (psIn->eComponentType == INOUT_COMPONENT_UINT32) + { + return SVT_UINT; + } + else if (psIn->eComponentType == INOUT_COMPONENT_SINT32) + { + return SVT_INT; + } + } + break; + } + case OPERAND_TYPE_CONSTANT_BUFFER: + { + ConstantBuffer* psCBuf = NULL; + ShaderVarType* psVarType = NULL; + int32_t index = -1; + int32_t rebase = -1; + int foundVar; + GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psContext->psShader->sInfo, &psCBuf); + if (psCBuf && !psCBuf->blob) + { + foundVar = GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], psOperand->aui32Swizzle, psCBuf, &psVarType, &index, &rebase); + if (foundVar && index == -1 && psOperand->psSubOperand[1] == NULL) + { + return psVarType->Type; + } + } + else + { + // Todo: this isn't correct yet. + return SVT_FLOAT; + } + break; + } + case OPERAND_TYPE_IMMEDIATE32: + { + return ePreferredTypeForImmediates; + } + + case OPERAND_TYPE_INPUT_THREAD_ID: + case OPERAND_TYPE_INPUT_THREAD_GROUP_ID: + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP: + case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: + { + return SVT_UINT; + } + case OPERAND_TYPE_SPECIAL_ADDRESS: + case OPERAND_TYPE_SPECIAL_LOOPCOUNTER: + { + return SVT_INT; + } + case OPERAND_TYPE_INPUT_GS_INSTANCE_ID: + { + return SVT_UINT; + } + case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: + { + return SVT_INT; + } + case OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID: + { + return SVT_INT; + } + default: + { + return SVT_FLOAT; + } + } + + return SVT_FLOAT; +} + +void TranslateOperandMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag) +{ + TranslateOperandWithMaskMETAL(psContext, psOperand, ui32TOFlag, OPERAND_4_COMPONENT_MASK_ALL); +} + +void TranslateOperandWithMaskMETAL(HLSLCrossCompilerContext* psContext, const Operand* psOperand, uint32_t ui32TOFlag, uint32_t ui32ComponentMask) +{ + bstring metal = *psContext->currentShaderString; + uint32_t ui32IgnoreSwizzle = 0; + + if (ui32TOFlag & TO_FLAG_NAME_ONLY) + { + METALTranslateVariableName(psContext, psOperand, ui32TOFlag, &ui32IgnoreSwizzle); + return; + } + + switch (psOperand->eModifier) + { + case OPERAND_MODIFIER_NONE: + { + break; + } + case OPERAND_MODIFIER_NEG: + { + bcatcstr(metal, "(-"); + break; + } + case OPERAND_MODIFIER_ABS: + { + bcatcstr(metal, "abs("); + break; + } + case OPERAND_MODIFIER_ABSNEG: + { + bcatcstr(metal, "-abs("); + break; + } + } + + METALMETALTranslateVariableNameWithMask(psContext, psOperand, ui32TOFlag, &ui32IgnoreSwizzle, ui32ComponentMask); + + switch (psOperand->eModifier) + { + case OPERAND_MODIFIER_NONE: + { + break; + } + case OPERAND_MODIFIER_NEG: + { + bcatcstr(metal, ")"); + break; + } + case OPERAND_MODIFIER_ABS: + { + bcatcstr(metal, ")"); + break; + } + case OPERAND_MODIFIER_ABSNEG: + { + bcatcstr(metal, ")"); + break; + } + } +} + +void ResourceNameMETAL(bstring targetStr, HLSLCrossCompilerContext* psContext, ResourceGroup group, const uint32_t ui32RegisterNumber, const int bZCompare) +{ + bstring metal = (targetStr == NULL) ? *psContext->currentShaderString : targetStr; + ResourceBinding* psBinding = 0; + int found; + + found = GetResourceFromBindingPoint(group, ui32RegisterNumber, &psContext->psShader->sInfo, &psBinding); + + if (found) + { + int i = 0; + char name[MAX_REFLECT_STRING_LENGTH]; + uint32_t ui32ArrayOffset = ui32RegisterNumber - psBinding->ui32BindPoint; + + while (psBinding->Name[i] != '\0' && i < (MAX_REFLECT_STRING_LENGTH - 1)) + { + name[i] = psBinding->Name[i]; + + //array syntax [X] becomes _0_ + //Otherwise declarations could end up as: + //uniform sampler2D SomeTextures[0]; + //uniform sampler2D SomeTextures[1]; + if (name[i] == '[' || name[i] == ']') + { + name[i] = '_'; + } + + ++i; + } + + name[i] = '\0'; + + if (ui32ArrayOffset) + { + bformata(metal, "%s%d", name, ui32ArrayOffset); + } + else + { + bformata(metal, "%s", name); + } + + if (RGROUP_SAMPLER == group) + { + if (bZCompare) + { + bcatcstr(metal, "_cmp"); + } + else + { + bcatcstr(metal, "_s"); + } + } + } + else + { + bformata(metal, "UnknownResource%d", ui32RegisterNumber); + } +} + +bstring TextureSamplerNameMETAL(ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare) +{ + bstring result; + ResourceBinding* psTextureBinding = 0; + ResourceBinding* psSamplerBinding = 0; + int foundTexture, foundSampler; + uint32_t i = 0; + char samplerName[MAX_REFLECT_STRING_LENGTH]; + uint32_t ui32ArrayOffset; + + foundTexture = GetResourceFromBindingPoint(RGROUP_TEXTURE, ui32TextureRegisterNumber, psShaderInfo, &psTextureBinding); + foundSampler = GetResourceFromBindingPoint(RGROUP_SAMPLER, ui32SamplerRegisterNumber, psShaderInfo, &psSamplerBinding); + + if (!foundTexture || !foundSampler) + { + result = bformat("UnknownResource%d_%d", ui32TextureRegisterNumber, ui32SamplerRegisterNumber); + return result; + } + + ui32ArrayOffset = ui32SamplerRegisterNumber - psSamplerBinding->ui32BindPoint; + + while (psSamplerBinding->Name[i] != '\0' && i < (MAX_REFLECT_STRING_LENGTH - 1)) + { + samplerName[i] = psSamplerBinding->Name[i]; + + //array syntax [X] becomes _0_ + //Otherwise declarations could end up as: + //uniform sampler2D SomeTextures[0]; + //uniform sampler2D SomeTextures[1]; + if (samplerName[i] == '[' || samplerName[i] == ']') + { + samplerName[i] = '_'; + } + + ++i; + } + samplerName[i] = '\0'; + + result = bfromcstr(""); + + + + if (ui32ArrayOffset) + { + bformata(result, "%s%d", samplerName, ui32ArrayOffset); + } + else + { + bformata(result, "%s", samplerName); + } + + if (bZCompare) + { + bcatcstr(result, "_cmp"); + } + else + { + bcatcstr(result, "_s"); + } + + return result; +} + +void ConcatTextureSamplerNameMETAL(bstring str, ShaderInfo* psShaderInfo, const uint32_t ui32TextureRegisterNumber, const uint32_t ui32SamplerRegisterNumber, const int bZCompare) +{ + bstring texturesamplername = TextureSamplerNameMETAL(psShaderInfo, ui32TextureRegisterNumber, ui32SamplerRegisterNumber, bZCompare); + bconcat(str, texturesamplername); + bdestroy(texturesamplername); +} + +uint32_t GetGmemInputResourceSlotMETAL(uint32_t const slotIn) +{ + if (slotIn >= GMEM_FLOAT4_START_SLOT) + { + return slotIn - GMEM_FLOAT4_START_SLOT; + } + if (slotIn >= GMEM_FLOAT3_START_SLOT) + { + return slotIn - GMEM_FLOAT3_START_SLOT; + } + if (slotIn >= GMEM_FLOAT2_START_SLOT) + { + return slotIn - GMEM_FLOAT2_START_SLOT; + } + if (slotIn >= GMEM_FLOAT_START_SLOT) + { + return slotIn - GMEM_FLOAT_START_SLOT; + } + return slotIn; +} + +uint32_t GetGmemInputResourceNumElementsMETAL(uint32_t const slotIn) +{ + if (slotIn >= GMEM_FLOAT4_START_SLOT) + { + return 4; + } + if (slotIn >= GMEM_FLOAT3_START_SLOT) + { + return 3; + } + if (slotIn >= GMEM_FLOAT2_START_SLOT) + { + return 2; + } + if (slotIn >= GMEM_FLOAT_START_SLOT) + { + return 1; + } + return 0; +}