yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie HermaszewskaCorrect include dir for libslang (#5539)7b570feed

master
11.2 KiB367 linesraw
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:
26    virtual SLANG_NO_THROW int SLANG_MCALL doThing(int a, int b) = 0;
27    virtual SLANG_NO_THROW int SLANG_MCALL calcHash(const char* in) = 0;
28};
29
30class ICountGood : public ISlangUnknown
31{
32public:
33    virtual SLANG_NO_THROW int SLANG_MCALL nextCount() = 0;
34};
35
36static int _calcHash(const char* in)
37{
38    int hash = 0;
39    for (; *in; ++in)
40    {
41        // A very poor hash function
42        hash = hash * 13 + *in;
43    }
44    return hash;
45}
46
47class DoThings : public IDoThings
48{
49public:
50    // We don't need queryInterface for this impl, or ref counting
51    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
52    queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE
53    {
54        return SLANG_E_NOT_IMPLEMENTED;
55    }
56    virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return 1; }
57    virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return 1; }
58
59    // IDoThings
60    virtual SLANG_NO_THROW int SLANG_MCALL doThing(int a, int b) SLANG_OVERRIDE
61    {
62        return a + b + 1;
63    }
64    virtual SLANG_NO_THROW int SLANG_MCALL calcHash(const char* in) SLANG_OVERRIDE
65    {
66        return (int)_calcHash(in);
67    }
68};
69
70class CountGood : public ICountGood
71{
72public:
73    // We don't need queryInterface for this impl, or ref counting
74    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
75    queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE
76    {
77        return SLANG_E_NOT_IMPLEMENTED;
78    }
79    virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return 1; }
80    virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return 1; }
81
82    // ICountGood
83    virtual SLANG_NO_THROW int SLANG_MCALL nextCount() SLANG_OVERRIDE { return m_count++; }
84
85    int m_count = 0;
86};
87
88struct ComTestContext
89{
90    ComTestContext(UnitTestContext* context)
91        : m_unitTestContext(context)
92    {
93        slang::IGlobalSession* slangSession = m_unitTestContext->slangGlobalSession;
94
95        m_defaultCppCompiler =
96            slangSession->getDefaultDownstreamCompiler(SLANG_SOURCE_LANGUAGE_CPP);
97
98        m_hostHostCallableCompiler = slangSession->getDownstreamCompilerForTransition(
99            SLANG_CPP_SOURCE,
100            SLANG_HOST_HOST_CALLABLE);
101        m_shaderHostCallableCompiler = slangSession->getDownstreamCompilerForTransition(
102            SLANG_CPP_SOURCE,
103            SLANG_SHADER_HOST_CALLABLE);
104    }
105
106    SlangResult 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
127        const bool hasLlvm =
128            SLANG_SUCCEEDED(slangSession->checkPassThroughSupport(SLANG_PASS_THROUGH_LLVM));
129
130        SlangPassThrough cppCompiler = SLANG_PASS_THROUGH_NONE;
131
132        {
133            const SlangPassThrough cppCompilers[] = {
134                SLANG_PASS_THROUGH_VISUAL_STUDIO,
135                SLANG_PASS_THROUGH_GCC,
136                SLANG_PASS_THROUGH_CLANG,
137            };
138            // Do we have a C++ compiler
139            for (const auto compiler : cppCompilers)
140            {
141                if (SLANG_SUCCEEDED(slangSession->checkPassThroughSupport(compiler)))
142                {
143                    cppCompiler = compiler;
144                    break;
145                }
146            }
147        }
148
149        // If we have an *actual* C++ compile rtest on that first
150        if (cppCompiler != SLANG_PASS_THROUGH_NONE)
151        {
152            slangSession->setDefaultDownstreamCompiler(SLANG_SOURCE_LANGUAGE_CPP, cppCompiler);
153
154            slangSession->setDownstreamCompilerForTransition(
155                SLANG_CPP_SOURCE,
156                SLANG_SHADER_HOST_CALLABLE,
157                cppCompiler);
158            slangSession->setDownstreamCompilerForTransition(
159                SLANG_CPP_SOURCE,
160                SLANG_HOST_HOST_CALLABLE,
161                cppCompiler);
162
163            SLANG_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
170        if (hasLlvm)
171        {
172            // Should run via slang-llvm
173            SLANG_RETURN_ON_FAIL(_runTest());
174        }
175
176        return SLANG_OK;
177    }
178
179    void _reset()
180    {
181        slang::IGlobalSession* slangSession = m_unitTestContext->slangGlobalSession;
182        slangSession->setDefaultDownstreamCompiler(SLANG_SOURCE_LANGUAGE_CPP, m_defaultCppCompiler);
183
184        slangSession->setDownstreamCompilerForTransition(
185            SLANG_CPP_SOURCE,
186            SLANG_SHADER_HOST_CALLABLE,
187            m_shaderHostCallableCompiler);
188        slangSession->setDownstreamCompilerForTransition(
189            SLANG_CPP_SOURCE,
190            SLANG_HOST_HOST_CALLABLE,
191            m_hostHostCallableCompiler);
192    }
193
194    ~ComTestContext() { _reset(); }
195
196    SlangResult _runTest();
197
198    UnitTestContext* m_unitTestContext;
199
200    SlangPassThrough m_defaultCppCompiler;
201    SlangPassThrough m_hostHostCallableCompiler;
202    SlangPassThrough m_shaderHostCallableCompiler;
203};
204
205SlangResult ComTestContext::_runTest()
206{
207    slang::IGlobalSession* slangSession = m_unitTestContext->slangGlobalSession;
208
209    // Create a compile request
210    Slang::ComPtr<slang::ICompileRequest> request;
211    SLANG_ALLOW_DEPRECATED_BEGIN
212    SLANG_RETURN_ON_FAIL(slangSession->createCompileRequest(request.writeRef()));
213    SLANG_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`.
219    const 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.
222    request->setTargetFlags(targetIndex, SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM);
223
224    request->setOptimizationLevel(SLANG_OPTIMIZATION_LEVEL_NONE);
225    request->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_STANDARD);
226
227    // Add the translation unit
228    const int translationUnitIndex =
229        request->addTranslationUnit(SLANG_SOURCE_LANGUAGE_SLANG, nullptr);
230
231    // Set the source file for the translation unit
232    request->addTranslationUnitSourceFile(
233        translationUnitIndex,
234        "tools/slang-unit-test/unit-test-com-host-callable.slang");
235
236    const 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    //
242    if (auto diagnostics = request->getDiagnosticOutput())
243    {
244        printf("%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).
249    ComPtr<ISlangSharedLibrary> sharedLibrary;
250    SLANG_RETURN_ON_FAIL(request->getTargetHostCallable(0, sharedLibrary.writeRef()));
251
252    {
253        typedef const char* (*Func)(const char*);
254        Func func = (Func)sharedLibrary->findFuncByName("getString");
255
256        if (!func)
257        {
258            return SLANG_FAIL;
259        }
260
261        String text = "Hello World!";
262        String returnedText = func(text.getBuffer());
263
264        SLANG_CHECK(text == returnedText);
265    }
266    {
267        typedef int (*Func)(const char* text, IDoThings* doThings);
268
269        Func func = (Func)sharedLibrary->findFuncByName("calcHash");
270
271        if (!func)
272        {
273            return SLANG_FAIL;
274        }
275
276        DoThings doThings;
277
278        String text("Hello");
279
280        const int hash = func(text.getBuffer(), &doThings);
281
282        SLANG_CHECK(hash == _calcHash(text.getBuffer()));
283    }
284
285    // Check accessing a global
286    {
287        typedef void (*SetFunc)(int v);
288        typedef int (*GetFunc)();
289
290        const auto setGlobal = (SetFunc)sharedLibrary->findFuncByName("setGlobal");
291        const auto getGlobal = (GetFunc)sharedLibrary->findFuncByName("getGlobal");
292
293        if (setGlobal == nullptr || getGlobal == nullptr)
294        {
295            return SLANG_FAIL;
296        }
297
298        // In the slang source it is set a default value
299        SLANG_CHECK(getGlobal() == 10);
300
301        for (Index i = 0; i < 10; ++i)
302        {
303            setGlobal(int(i));
304            SLANG_CHECK(getGlobal() == i);
305        }
306    }
307
308    // Check using a global interface
309    {
310
311        typedef void (*SetCounterFunc)(ICountGood* counter);
312        typedef int (*NextCountFunc)();
313
314        const auto setCounter = (SetCounterFunc)sharedLibrary->findFuncByName("setCounter");
315        const auto nextCount = (NextCountFunc)sharedLibrary->findFuncByName("nextCount");
316
317        if (setCounter == nullptr || nextCount == nullptr)
318        {
319            return SLANG_FAIL;
320        }
321
322        CountGood counter;
323
324        ICountGood* counterIntf = &counter;
325
326        setCounter(counterIntf);
327
328        auto counterPtr = (ICountGood**)sharedLibrary->findSymbolAddressByName("globalCounter");
329        SLANG_CHECK(counterPtr);
330        if (!counterPtr)
331        {
332            return SLANG_FAIL;
333        }
334
335        for (Index i = 0; i < 10; ++i)
336        {
337            SLANG_CHECK(*counterPtr == &counter);
338
339            const auto v = nextCount();
340            SLANG_CHECK(v == i);
341        }
342    }
343
344    return 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
359    return;
360#endif
361
362    ComTestContext context(unitTestContext);
363
364    const auto result = context.runTests();
365
366    SLANG_CHECK(SLANG_SUCCEEDED(result));
367}