yum-mirror/slang

Making it easier to work with shaders

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

Anders LeinoEnable some rendering tests (#5623)dcc7c6f00

master
7.8 KiB309 linesraw
1
2#include "slang-render-api-util.h"
3
4#include "slang-list.h"
5#include "slang-platform.h"
6#include "slang-string-util.h"
7#include "slang.h"
8
9namespace Slang
10{
11
12// NOTE! Must keep in same order as RenderApiType and have same amount of entries
13/* static */ const RenderApiUtil::Info RenderApiUtil::s_infos[] = {
14    {RenderApiType::Vulkan, "vk,vulkan", ""},
15    {RenderApiType::D3D12, "dx12,d3d12", ""},
16    {RenderApiType::D3D11, "dx11,d3d11", "hlsl,hlsl-rewrite,glsl-rewrite,glsl-cross,slang"},
17    {RenderApiType::Metal, "mtl,metal", ""},
18    {RenderApiType::CPU, "cpu", ""},
19    {RenderApiType::CUDA, "cuda", "cuda,ptx"},
20    {RenderApiType::WebGPU, "wgpu,webgpu", "wgsl"},
21};
22
23static int _calcAvailableApis()
24{
25    int flags = 0;
26    for (int i = 0; i < int(RenderApiType::CountOf); i++)
27    {
28        if (RenderApiUtil::calcHasApi(RenderApiType(i)))
29        {
30            flags |= (1 << i);
31        }
32    }
33
34    return flags;
35}
36
37/* static */ int RenderApiUtil::getAvailableApis()
38{
39    static int s_availableApis = _calcAvailableApis();
40    return s_availableApis;
41}
42
43UnownedStringSlice RenderApiUtil::getApiName(RenderApiType type)
44{
45    int index = int(type);
46    if (index < 0 || index >= int(RenderApiType::CountOf))
47    {
48        return UnownedStringSlice();
49    }
50    SLANG_ASSERT(s_infos[index].type == type);
51    return StringUtil::getAtInSplit(UnownedStringSlice(s_infos[index].names), ',', 0);
52}
53
54/* static */ RenderApiType RenderApiUtil::findApiTypeByName(const Slang::UnownedStringSlice& name)
55{
56    using namespace Slang;
57    List<UnownedStringSlice> namesList;
58    for (Index j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++)
59    {
60        const auto& apiInfo = RenderApiUtil::s_infos[j];
61        const UnownedStringSlice names(apiInfo.names);
62
63        if (names.indexOf(',') >= 0)
64        {
65            StringUtil::split(names, ',', namesList);
66            if (namesList.indexOf(name) != Index(-1))
67            {
68                return apiInfo.type;
69            }
70        }
71        else if (names == name)
72        {
73            return apiInfo.type;
74        }
75    }
76    return RenderApiType::Unknown;
77}
78
79/* static */ Slang::Result RenderApiUtil::findApiFlagsByName(
80    const Slang::UnownedStringSlice& name,
81    RenderApiFlags* flagsOut)
82{
83    // Special case 'all'
84    if (name == "all")
85    {
86        *flagsOut = RenderApiFlags(RenderApiFlag::AllOf);
87        return SLANG_OK;
88    }
89    if (name == "none")
90    {
91        *flagsOut = RenderApiFlags(0);
92        return SLANG_OK;
93    }
94    RenderApiType type = findApiTypeByName(name);
95    if (type == RenderApiType::Unknown)
96    {
97        return SLANG_FAIL;
98    }
99    *flagsOut = RenderApiFlags(1) << int(type);
100    return SLANG_OK;
101}
102
103static bool isNameStartChar(char c)
104{
105    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_');
106}
107
108static bool isNameNextChar(char c)
109{
110    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c >= '0' && c <= '9');
111}
112
113namespace
114{ // anonymous
115enum class Token
116{
117    eError,
118    eOp,
119    eId,
120    eEnd,
121};
122} // namespace
123
124static Token nextToken(Slang::UnownedStringSlice& textInOut, Slang::UnownedStringSlice& lexemeOut)
125{
126    using namespace Slang;
127    if (textInOut.getLength() <= 0)
128    {
129        return Token::eEnd;
130    }
131    const char* start = textInOut.begin();
132    const char* end = textInOut.end();
133
134    const char firstChar = start[0];
135    if (firstChar == '-' || firstChar == '+')
136    {
137        lexemeOut = UnownedStringSlice(start, start + 1);
138        textInOut = UnownedStringSlice(start + 1, end);
139        return Token::eOp;
140    }
141
142    if (!isNameStartChar(firstChar))
143    {
144        lexemeOut = UnownedStringSlice(start, start + 1);
145        return Token::eError;
146    }
147    const char* cur = start + 1;
148    while (cur < end && isNameNextChar(*cur))
149    {
150        cur++;
151    }
152
153    lexemeOut = UnownedStringSlice(start, cur);
154    textInOut = UnownedStringSlice(cur, end);
155    return Token::eId;
156}
157
158/* static */ Slang::Result RenderApiUtil::parseApiFlags(
159    const Slang::UnownedStringSlice& textIn,
160    RenderApiFlags initialFlags,
161    RenderApiFlags* apiFlagsOut)
162{
163    using namespace Slang;
164
165    UnownedStringSlice text(textIn);
166    UnownedStringSlice lexeme;
167
168    RenderApiFlags apiFlags = 0;
169
170    switch (nextToken(text, lexeme))
171    {
172    case Token::eOp:
173        {
174            // If we start with an op - we use the passed in values as the default
175            // Rewind back to the start
176            text = textIn;
177            apiFlags = initialFlags;
178            break;
179        }
180    case Token::eId:
181        {
182            // If we start with an Id - we use that as the starting state
183            SLANG_RETURN_ON_FAIL(findApiFlagsByName(lexeme, &apiFlags));
184            break;
185        }
186    default:
187        return SLANG_FAIL;
188    }
189
190    while (true)
191    {
192        // Must have an op followed by an id unless we are at the end
193        switch (nextToken(text, lexeme))
194        {
195        case Token::eEnd:
196            {
197                *apiFlagsOut = apiFlags;
198                return SLANG_OK;
199            }
200        case Token::eOp:
201            break;
202        default:
203            return SLANG_FAIL;
204        }
205
206        const char op = lexeme[0];
207        if (nextToken(text, lexeme) != Token::eId)
208        {
209            return SLANG_FAIL;
210        }
211
212        RenderApiFlags flags;
213        SLANG_RETURN_ON_FAIL(findApiFlagsByName(lexeme, &flags));
214
215        if (op == '+')
216        {
217            apiFlags |= flags;
218        }
219        else
220        {
221            apiFlags &= ~flags;
222        }
223    }
224}
225
226/* static */ RenderApiType RenderApiUtil::findRenderApiType(const Slang::UnownedStringSlice& text)
227{
228    using namespace Slang;
229    for (Index j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++)
230    {
231        const auto& apiInfo = RenderApiUtil::s_infos[j];
232        if (StringUtil::indexOfInSplit(UnownedStringSlice(apiInfo.names), ',', text) >= 0)
233        {
234            return apiInfo.type;
235        }
236    }
237    // Didn't find any
238    return RenderApiType::Unknown;
239}
240
241/* static */ RenderApiType RenderApiUtil::findImplicitLanguageRenderApiType(
242    const Slang::UnownedStringSlice& text)
243{
244    using namespace Slang;
245    for (Index j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++)
246    {
247        const auto& apiInfo = RenderApiUtil::s_infos[j];
248        if (StringUtil::indexOfInSplit(UnownedStringSlice(apiInfo.languageNames), ',', text) >= 0)
249        {
250            return apiInfo.type;
251        }
252    }
253    // Didn't find any
254    return RenderApiType::Unknown;
255}
256
257#if SLANG_ENABLE_DIRECTX
258static bool _canLoadSharedLibrary(const char* libName)
259{
260    SharedLibrary::Handle handle;
261    SlangResult res = SharedLibrary::load(libName, handle);
262    if (SLANG_FAILED(res))
263    {
264        return false;
265    }
266    SharedLibrary::unload(handle);
267    return true;
268}
269#endif
270
271/* static */ bool RenderApiUtil::calcHasApi(RenderApiType type)
272{
273    switch (type)
274    {
275#if SLANG_WINDOWS_FAMILY
276    case RenderApiType::Vulkan:
277        return _canLoadSharedLibrary("vulkan-1") || _canLoadSharedLibrary("vk_swiftshader");
278    case RenderApiType::WebGPU:
279        return _canLoadSharedLibrary("webgpu_dawn") && _canLoadSharedLibrary("dxcompiler") &&
280               _canLoadSharedLibrary("dxil");
281#elif SLANG_APPLE_FAMILY
282    case RenderApiType::Vulkan:
283        return true;
284    case RenderApiType::Metal:
285        return true;
286#elif SLANG_UNIX_FAMILY
287    case RenderApiType::Vulkan:
288        return true;
289#endif
290
291#if SLANG_ENABLE_DIRECTX
292    case RenderApiType::D3D11:
293        return _canLoadSharedLibrary(SLANG_ENABLE_DXVK ? "dxvk_d3d11" : "d3d11");
294    case RenderApiType::D3D12:
295        return _canLoadSharedLibrary(SLANG_ENABLE_VKD3D ? "vkd3d-proton-d3d12" : "d3d12");
296#endif
297
298    case RenderApiType::CPU:
299        return true;
300    // We'll assume CUDA is available, and if not, trying to create it will detect it
301    case RenderApiType::CUDA:
302        return true;
303    default:
304        break;
305    }
306    return false;
307}
308
309} // namespace Slang