yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
7b570feed
master
1// unit-test-com-host-callable.cpp 2 3#include "../../source/core/slang-byte-encode-util.h" 4#include "../../source/core/slang-list.h" 5#include "slang-com-helper.h" 6#include "slang-com-ptr.h" 7#include "slang.h" 8#include "unit-test/slang-unit-test.h" 9 10#include <stdio.h> 11#include <stdlib.h> 12 13namespace 14{// anonymous 15 16// Slang namespace is used for elements support code (like core) which we use here 17// for ComPtr<> and TestToolUtil 18using namespace Slang ; 19 20// For the moment we have to explicitly write the Slang COM interface in C++ code. It *MUST* match 21// the interface in the slang source 22// As it stands all interfaces need to derive from ISlangUnknown (or IUnknown). 23class IDoThings :public ISlangUnknown 24{ 25public : 26virtual SLANG_NO_THROW int SLANG_MCALL doThing (int a ,int b )= 0 ; 27virtual SLANG_NO_THROW int SLANG_MCALL calcHash (const char * in )= 0 ; 28}; 29 30class ICountGood :public ISlangUnknown 31{ 32public : 33virtual SLANG_NO_THROW int SLANG_MCALL nextCount ()= 0 ; 34}; 35 36static int _calcHash (const char * in ) 37{ 38int hash = 0 ; 39for (;* in ;++ in ) 40 { 41// A very poor hash function 42hash = hash * 13 + * in ; 43 } 44return hash ; 45} 46 47class DoThings :public IDoThings 48{ 49public : 50// We don't need queryInterface for this impl, or ref counting 51virtual SLANG_NO_THROW SlangResult SLANG_MCALL 52queryInterface (SlangUUID const & uuid ,void ** outObject )SLANG_OVERRIDE 53 { 54return SLANG_E_NOT_IMPLEMENTED ; 55 } 56virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef ()SLANG_OVERRIDE {return 1 ; } 57virtual SLANG_NO_THROW uint32_t SLANG_MCALL release ()SLANG_OVERRIDE {return 1 ; } 58 59// IDoThings 60virtual SLANG_NO_THROW int SLANG_MCALL doThing (int a ,int b )SLANG_OVERRIDE 61 { 62return a + b + 1 ; 63 } 64virtual SLANG_NO_THROW int SLANG_MCALL calcHash (const char * in )SLANG_OVERRIDE 65 { 66return (int )_calcHash (in ); 67 } 68}; 69 70class CountGood :public ICountGood 71{ 72public : 73// We don't need queryInterface for this impl, or ref counting 74virtual SLANG_NO_THROW SlangResult SLANG_MCALL 75queryInterface (SlangUUID const & uuid ,void ** outObject )SLANG_OVERRIDE 76 { 77return SLANG_E_NOT_IMPLEMENTED ; 78 } 79virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef ()SLANG_OVERRIDE {return 1 ; } 80virtual SLANG_NO_THROW uint32_t SLANG_MCALL release ()SLANG_OVERRIDE {return 1 ; } 81 82// ICountGood 83virtual SLANG_NO_THROW int SLANG_MCALL nextCount ()SLANG_OVERRIDE {return m_count ++ ; } 84 85int m_count = 0 ; 86}; 87 88struct ComTestContext 89{ 90ComTestContext (UnitTestContext * context ) 91 :m_unitTestContext (context ) 92 { 93 slang::IGlobalSession * slangSession = m_unitTestContext -> slangGlobalSession ; 94 95m_defaultCppCompiler = 96slangSession -> getDefaultDownstreamCompiler (SLANG_SOURCE_LANGUAGE_CPP ); 97 98m_hostHostCallableCompiler = slangSession -> getDownstreamCompilerForTransition ( 99SLANG_CPP_SOURCE , 100SLANG_HOST_HOST_CALLABLE ); 101m_shaderHostCallableCompiler = slangSession -> getDownstreamCompilerForTransition ( 102SLANG_CPP_SOURCE , 103SLANG_SHADER_HOST_CALLABLE ); 104 } 105 106SlangResult runTests () 107 { 108 slang::IGlobalSession * slangSession = m_unitTestContext -> slangGlobalSession ; 109 110// TODO(JS): 111// Care is needed around this in normal testing. `slang-llvm` is whatever was asked for for 112// when premake was built when the target is specified. Otherwise it is the `default` which 113// is typically 64 bit during development. 114// 115// On CI we should be okay, because it should download the correct `slang-llvm` for the 116// build (as it packages up with it). But for normal development, that can easily not be the 117// case (for example changing to 32 bit build in VS is a problem). 118// 119// Make sure to run 120// 121// ``` 122// premake --arch=x86 --deps=true 123// ``` 124// 125// for the actual target/arch(!) 126 127const bool hasLlvm = 128SLANG_SUCCEEDED (slangSession -> checkPassThroughSupport (SLANG_PASS_THROUGH_LLVM )); 129 130SlangPassThrough cppCompiler = SLANG_PASS_THROUGH_NONE ; 131 132 { 133const SlangPassThrough cppCompilers []= { 134SLANG_PASS_THROUGH_VISUAL_STUDIO , 135SLANG_PASS_THROUGH_GCC , 136SLANG_PASS_THROUGH_CLANG , 137 }; 138// Do we have a C++ compiler 139for (const auto compiler :cppCompilers ) 140 { 141if (SLANG_SUCCEEDED (slangSession -> checkPassThroughSupport (compiler ))) 142 { 143cppCompiler = compiler ; 144break ; 145 } 146 } 147 } 148 149// If we have an *actual* C++ compile rtest on that first 150if (cppCompiler != SLANG_PASS_THROUGH_NONE ) 151 { 152slangSession -> setDefaultDownstreamCompiler (SLANG_SOURCE_LANGUAGE_CPP ,cppCompiler ); 153 154slangSession -> setDownstreamCompilerForTransition ( 155SLANG_CPP_SOURCE , 156SLANG_SHADER_HOST_CALLABLE , 157cppCompiler ); 158slangSession -> setDownstreamCompilerForTransition ( 159SLANG_CPP_SOURCE , 160SLANG_HOST_HOST_CALLABLE , 161cppCompiler ); 162 163SLANG_RETURN_ON_FAIL (_runTest ()); 164 } 165 166// Reset the compiler that's used for host-callable 167_reset (); 168 169// If we have Llvm it is the default host callable compiler 170if (hasLlvm ) 171 { 172// Should run via slang-llvm 173SLANG_RETURN_ON_FAIL (_runTest ()); 174 } 175 176return SLANG_OK ; 177 } 178 179void _reset () 180 { 181 slang::IGlobalSession * slangSession = m_unitTestContext -> slangGlobalSession ; 182slangSession -> setDefaultDownstreamCompiler (SLANG_SOURCE_LANGUAGE_CPP ,m_defaultCppCompiler ); 183 184slangSession -> setDownstreamCompilerForTransition ( 185SLANG_CPP_SOURCE , 186SLANG_SHADER_HOST_CALLABLE , 187m_shaderHostCallableCompiler ); 188slangSession -> setDownstreamCompilerForTransition ( 189SLANG_CPP_SOURCE , 190SLANG_HOST_HOST_CALLABLE , 191m_hostHostCallableCompiler ); 192 } 193 194 ~ComTestContext () {_reset (); } 195 196SlangResult _runTest (); 197 198UnitTestContext * m_unitTestContext ; 199 200SlangPassThrough m_defaultCppCompiler ; 201SlangPassThrough m_hostHostCallableCompiler ; 202SlangPassThrough m_shaderHostCallableCompiler ; 203}; 204 205SlangResult ComTestContext ::_runTest () 206{ 207 slang::IGlobalSession * slangSession = m_unitTestContext -> slangGlobalSession ; 208 209// Create a compile request 210Slang ::ComPtr < slang::ICompileRequest > request ; 211SLANG_ALLOW_DEPRECATED_BEGIN 212SLANG_RETURN_ON_FAIL (slangSession -> createCompileRequest (request .writeRef ())); 213SLANG_ALLOW_DEPRECATED_END 214 215// We want to compile to 'HOST_CALLABLE' here such that we can execute the Slang code. 216// 217// Note that it is possible to use HOST_HOST_CALLABLE, but this currently only works with 218// 'regular' C++ compilers not with `slang-llvm`. 219const int targetIndex = request -> addCodeGenTarget (SLANG_SHADER_HOST_CALLABLE ); 220 221// Set the target flag to indicate that we want to compile all into a library. 222request -> setTargetFlags (targetIndex ,SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM ); 223 224request -> setOptimizationLevel (SLANG_OPTIMIZATION_LEVEL_NONE ); 225request -> setDebugInfoLevel (SLANG_DEBUG_INFO_LEVEL_STANDARD ); 226 227// Add the translation unit 228const int translationUnitIndex = 229request -> addTranslationUnit (SLANG_SOURCE_LANGUAGE_SLANG ,nullptr ); 230 231// Set the source file for the translation unit 232request -> addTranslationUnitSourceFile ( 233translationUnitIndex , 234"tools/slang-unit-test/unit-test-com-host-callable.slang" ); 235 236const SlangResult compileRes = request -> compile (); 237 238// Even if there were no errors that forced compilation to fail, the 239// compiler may have produced "diagnostic" output such as warnings. 240// We will go ahead and print that output here. 241// 242if (auto diagnostics = request -> getDiagnosticOutput ()) 243 { 244printf ("%s" ,diagnostics ); 245 } 246 247// Get the 'shared library' (note that this doesn't necessarily have to be implemented as a 248// shared library it's just an interface to executable code). 249ComPtr < ISlangSharedLibrary > sharedLibrary ; 250SLANG_RETURN_ON_FAIL (request -> getTargetHostCallable (0 ,sharedLibrary .writeRef ())); 251 252 { 253typedef const char * (* Func )(const char * ); 254Func func = (Func )sharedLibrary -> findFuncByName ("getString" ); 255 256if (!func ) 257 { 258return SLANG_FAIL ; 259 } 260 261String text = "Hello World!" ; 262String returnedText = func (text .getBuffer ()); 263 264SLANG_CHECK (text == returnedText ); 265 } 266 { 267typedef int (* Func )(const char * text ,IDoThings * doThings ); 268 269Func func = (Func )sharedLibrary -> findFuncByName ("calcHash" ); 270 271if (!func ) 272 { 273return SLANG_FAIL ; 274 } 275 276DoThings doThings ; 277 278String text ("Hello" ); 279 280const int hash = func (text .getBuffer (),& doThings ); 281 282SLANG_CHECK (hash == _calcHash (text .getBuffer ())); 283 } 284 285// Check accessing a global 286 { 287typedef void (* SetFunc )(int v ); 288typedef int (* GetFunc )(); 289 290const auto setGlobal = (SetFunc )sharedLibrary -> findFuncByName ("setGlobal" ); 291const auto getGlobal = (GetFunc )sharedLibrary -> findFuncByName ("getGlobal" ); 292 293if (setGlobal == nullptr || getGlobal == nullptr ) 294 { 295return SLANG_FAIL ; 296 } 297 298// In the slang source it is set a default value 299SLANG_CHECK (getGlobal ()== 10 ); 300 301for (Index i = 0 ;i < 10 ;++ i ) 302 { 303setGlobal (int (i )); 304SLANG_CHECK (getGlobal ()== i ); 305 } 306 } 307 308// Check using a global interface 309 { 310 311typedef void (* SetCounterFunc )(ICountGood * counter ); 312typedef int (* NextCountFunc )(); 313 314const auto setCounter = (SetCounterFunc )sharedLibrary -> findFuncByName ("setCounter" ); 315const auto nextCount = (NextCountFunc )sharedLibrary -> findFuncByName ("nextCount" ); 316 317if (setCounter == nullptr || nextCount == nullptr ) 318 { 319return SLANG_FAIL ; 320 } 321 322CountGood counter ; 323 324ICountGood * counterIntf = & counter ; 325 326setCounter (counterIntf ); 327 328auto counterPtr = (ICountGood ** )sharedLibrary -> findSymbolAddressByName ("globalCounter" ); 329SLANG_CHECK (counterPtr ); 330if (!counterPtr ) 331 { 332return SLANG_FAIL ; 333 } 334 335for (Index i = 0 ;i < 10 ;++ i ) 336 { 337SLANG_CHECK (* counterPtr == & counter ); 338 339const auto v = nextCount (); 340SLANG_CHECK (v == i ); 341 } 342 } 343 344return SLANG_OK ; 345} 346 347}// namespace 348 349SLANG_UNIT_TEST (comHostCallable ) 350{ 351#if SLANG_PTR_IS_32 && !SLANG_MICROSOFT_FAMILY 352// TODO(JS): 353// We can't currently run this test reliably on targets other than windows 354// Visual Studio DownstreamCompiler has support for 32 bit builds 355// Other targets generally build for the native environment which is almost always 64 bit, 356// and it requires other features to build/test 32 bit binaries on such systems. 357// 358// So we disable for any 32 bit non MS target for now 359return ; 360#endif 361 362ComTestContext context (unitTestContext ); 363 364const auto result = context .runTests (); 365 366SLANG_CHECK (SLANG_SUCCEEDED (result )); 367}