yum-mirror/slang

Making it easier to work with shaders

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

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
11.3 KiB370 linesraw
1// slang-artifact-handler-impl.cpp
2#include "slang-artifact-handler-impl.h"
3
4#include "../core/slang-castable.h"
5#include "../core/slang-file-system.h"
6#include "../core/slang-io.h"
7#include "../core/slang-shared-library.h"
8#include "slang-artifact-desc-util.h"
9#include "slang-artifact-helper.h"
10#include "slang-artifact-impl.h"
11#include "slang-artifact-representation-impl.h"
12#include "slang-artifact-util.h"
13#include "slang-json-source-map-util.h"
14#include "slang-slice-allocator.h"
15#include "slang-source-map.h"
16
17namespace Slang
18{
19
20/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! DefaultArtifactHandler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
21
22/* static */ DefaultArtifactHandler DefaultArtifactHandler::g_singleton;
23
24SlangResult DefaultArtifactHandler::queryInterface(SlangUUID const& uuid, void** outObject)
25{
26    if ([[maybe_unused]] auto ptr = getInterface(uuid))
27    {
28        SLANG_ASSERT(ptr == this);
29        addRef();
30        *outObject = static_cast<IArtifactHandler*>(this);
31        return SLANG_OK;
32    }
33    return SLANG_E_NO_INTERFACE;
34}
35
36void* DefaultArtifactHandler::castAs(const Guid& guid)
37{
38    if (auto ptr = getInterface(guid))
39    {
40        return ptr;
41    }
42    return getObject(guid);
43}
44
45void* DefaultArtifactHandler::getInterface(const Guid& uuid)
46{
47    if (uuid == ISlangUnknown::getTypeGuid() || uuid == ICastable::getTypeGuid() ||
48        uuid == IArtifactHandler::getTypeGuid())
49    {
50        return static_cast<IArtifactHandler*>(this);
51    }
52
53    return nullptr;
54}
55
56void* DefaultArtifactHandler::getObject(const Guid& uuid)
57{
58    SLANG_UNUSED(uuid);
59    return nullptr;
60}
61
62SlangResult DefaultArtifactHandler::_addRepresentation(
63    IArtifact* artifact,
64    ArtifactKeep keep,
65    ISlangUnknown* rep,
66    ICastable** outCastable)
67{
68    SLANG_ASSERT(rep);
69
70    // See if it implements ICastable
71    {
72        ComPtr<ICastable> castable;
73        if (SLANG_SUCCEEDED(rep->queryInterface(SLANG_IID_PPV_ARGS(castable.writeRef()))) &&
74            castable)
75        {
76            return _addRepresentation(artifact, keep, castable, outCastable);
77        }
78    }
79
80    // We have to wrap
81    ComPtr<IUnknownCastableAdapter> adapter(new UnknownCastableAdapter(rep));
82    return _addRepresentation(artifact, keep, adapter, outCastable);
83}
84
85SlangResult DefaultArtifactHandler::_addRepresentation(
86    IArtifact* artifact,
87    ArtifactKeep keep,
88    ICastable* castable,
89    ICastable** outCastable)
90{
91    SLANG_ASSERT(castable);
92
93    if (canKeep(keep))
94    {
95        artifact->addRepresentation(castable);
96    }
97
98    castable->addRef();
99    *outCastable = castable;
100    return SLANG_OK;
101}
102
103SlangResult DefaultArtifactHandler::expandChildren(IArtifact* container)
104{
105    // First check if it has already been expanded
106    SlangResult res = container->getExpandChildrenResult();
107    if (res != SLANG_E_UNINITIALIZED)
108    {
109        // It's already expanded
110        return res;
111    }
112
113    // For the generic container type, we just expand as empty
114    const auto desc = container->getDesc();
115    if (desc.kind == ArtifactKind::Container)
116    {
117        // If it's just a generic container, we can assume it's expanded, and be done
118        return SLANG_OK;
119    }
120
121    if (isDerivedFrom(desc.kind, ArtifactKind::Container))
122    {
123        // TODO(JS):
124        // Proper implementation should (for example) be able to expand a Zip file etc.
125
126        // For now we just set that there are no children, and be done
127        container->setChildren(nullptr, 0);
128        return SLANG_E_NOT_IMPLEMENTED;
129    }
130
131    // We can't expand non container like types, so we just make sure it's empty
132    container->setChildren(nullptr, 0);
133    return SLANG_OK;
134}
135
136static SlangResult _loadSourceMap(
137    IArtifact* artifact,
138    ArtifactKeep intermediateKeep,
139    SourceMap& outSourceMap)
140{
141    const auto desc = artifact->getDesc();
142    if (isDerivedFrom(desc.kind, ArtifactKind::Json) &&
143        isDerivedFrom(desc.payload, ArtifactPayload::SourceMap))
144    {
145        ComPtr<ISlangBlob> blob;
146        SLANG_RETURN_ON_FAIL(artifact->loadBlob(intermediateKeep, blob.writeRef()));
147
148        SLANG_RETURN_ON_FAIL(JSONSourceMapUtil::read(blob, outSourceMap));
149        return SLANG_OK;
150    }
151
152    return SLANG_FAIL;
153}
154
155SlangResult DefaultArtifactHandler::getOrCreateRepresentation(
156    IArtifact* artifact,
157    const Guid& guid,
158    ArtifactKeep keep,
159    ICastable** outCastable)
160{
161    const auto reps = artifact->getRepresentations();
162
163    // See if we already have a rep of this type
164    for (ICastable* rep : reps)
165    {
166        if (rep->castAs(guid))
167        {
168            rep->addRef();
169            *outCastable = rep;
170            return SLANG_OK;
171        }
172    }
173
174    // We can ask each representation if they can do the conversion to the type, if they can we just
175    // use that
176    for (ICastable* castable : reps)
177    {
178        if (auto rep = as<IArtifactRepresentation>(castable))
179        {
180            ComPtr<ICastable> created;
181            if (SLANG_SUCCEEDED(rep->createRepresentation(guid, created.writeRef())))
182            {
183                SLANG_ASSERT(created);
184                // Add the rep
185                return _addRepresentation(artifact, keep, created, outCastable);
186            }
187        }
188    }
189
190    // Special cases
191    if (guid == SourceMap::getTypeGuid())
192    {
193        // Blob -> SourceMap
194        ComPtr<IBoxValue<SourceMap>> sourceMap(new BoxValue<SourceMap>);
195        SLANG_RETURN_ON_FAIL(_loadSourceMap(artifact, getIntermediateKeep(keep), sourceMap->get()));
196        return _addRepresentation(artifact, keep, sourceMap, outCastable);
197    }
198    else if (guid == ISlangSharedLibrary::getTypeGuid())
199    {
200        ComPtr<ISlangSharedLibrary> sharedLib;
201        SLANG_RETURN_ON_FAIL(_loadSharedLibrary(artifact, sharedLib.writeRef()));
202        return _addRepresentation(artifact, keep, sharedLib, outCastable);
203    }
204    else if (guid == IOSFileArtifactRepresentation::getTypeGuid())
205    {
206        ComPtr<IOSFileArtifactRepresentation> fileRep;
207        SLANG_RETURN_ON_FAIL(
208            _createOSFile(artifact, getIntermediateKeep(keep), fileRep.writeRef()));
209        return _addRepresentation(artifact, keep, fileRep, outCastable);
210    }
211
212    // Handle known conversion to blobs
213    if (guid == ISlangBlob::getTypeGuid())
214    {
215        if (auto sourceMap = findRepresentation<SourceMap>(artifact))
216        {
217            // SourceMap -> Blob
218            ComPtr<ISlangBlob> blob;
219            SLANG_RETURN_ON_FAIL(JSONSourceMapUtil::write(*sourceMap, blob));
220            // Add the rep
221            return _addRepresentation(artifact, keep, blob, outCastable);
222        }
223    }
224
225    return SLANG_E_NOT_AVAILABLE;
226}
227
228SlangResult DefaultArtifactHandler::_createOSFile(
229    IArtifact* artifact,
230    ArtifactKeep keep,
231    IOSFileArtifactRepresentation** outFileRep)
232{
233    // Look if we have an IExtFile representation, as we might already have a OS file associated
234    // with that
235    if (auto extRep = findRepresentation<IExtFileArtifactRepresentation>(artifact))
236    {
237        auto system = extRep->getFileSystem();
238
239        String path;
240        switch (system->getOSPathKind())
241        {
242        case OSPathKind::Direct:
243            {
244                path = UnownedStringSlice(extRep->getPath());
245                break;
246            }
247        case OSPathKind::OperatingSystem:
248            {
249                ComPtr<ISlangBlob> osPathBlob;
250                if (SLANG_SUCCEEDED(system->getPath(
251                        PathKind::OperatingSystem,
252                        extRep->getPath(),
253                        osPathBlob.writeRef())))
254                {
255                    path = StringUtil::getString(osPathBlob);
256                }
257                break;
258            }
259        default:
260            break;
261        }
262
263        if (path.getLength())
264        {
265            auto fileRep = OSFileArtifactRepresentation::create(
266                IOSFileArtifactRepresentation::Kind::Reference,
267                path.getUnownedSlice(),
268                nullptr);
269            // As a sanity check make sure it exists!
270            if (fileRep->exists())
271            {
272                *outFileRep = fileRep.detach();
273                return SLANG_OK;
274            }
275        }
276    }
277
278    // Okay looks like we will need to generate a temporary file
279    auto helper = DefaultArtifactHelper::getSingleton();
280
281    // If we are going to access as a file we need to be able to write it, and to do that we need a
282    // blob
283    ComPtr<ISlangBlob> blob;
284    SLANG_RETURN_ON_FAIL(artifact->loadBlob(getIntermediateKeep(keep), blob.writeRef()));
285
286    // Find some name associated
287    auto name = ArtifactUtil::findName(artifact);
288    if (name.getLength() == 0)
289    {
290        name = toSlice("unknown");
291    }
292
293    // Okay we need to store as a temporary. Get a lock file.
294    ComPtr<IOSFileArtifactRepresentation> lockFile;
295    SLANG_RETURN_ON_FAIL(helper->createLockFile(asCharSlice(name), lockFile.writeRef()));
296
297    // Now we need the appropriate name for this item
298    ComPtr<ISlangBlob> pathBlob;
299    SLANG_RETURN_ON_FAIL(
300        helper->calcArtifactPath(artifact, lockFile->getPath(), pathBlob.writeRef()));
301
302    const auto path = StringUtil::getSlice(pathBlob);
303
304    // Write the contents
305    SLANG_RETURN_ON_FAIL(
306        File::writeAllBytes(path, blob->getBufferPointer(), blob->getBufferSize()));
307    if (artifact->getDesc().kind == ArtifactKind::Executable)
308    {
309        SLANG_RETURN_ON_FAIL(File::makeExecutable(path));
310    }
311
312    ComPtr<IOSFileArtifactRepresentation> fileRep;
313
314    // TODO(JS): This path comparison is perhaps not perfect, in that it assumes the path is not
315    // changed in any way. For example an impl of calcArtifactPath that changed slashes or used a
316    // canonical path might mean the lock file and the rep have the same path. As it stands
317    // calcArtifactPath impl doesn't do that, but that is perhaps somewhatfragile
318
319    // If the paths are identical, we can just use the lock file for the rep
320    if (UnownedStringSlice(lockFile->getPath()) == path)
321    {
322        fileRep.swap(lockFile);
323    }
324    else
325    {
326        // Create a new rep that references the lock file
327        fileRep = new OSFileArtifactRepresentation(
328            IOSFileArtifactRepresentation::Kind::Owned,
329            path,
330            lockFile);
331    }
332
333    // Return the file
334    *outFileRep = fileRep.detach();
335    return SLANG_OK;
336}
337
338SlangResult DefaultArtifactHandler::_loadSharedLibrary(
339    IArtifact* artifact,
340    ISlangSharedLibrary** outSharedLibrary)
341{
342    // If it is 'shared library' for a CPU like thing, we can try and load it
343    const auto desc = artifact->getDesc();
344    if ((isDerivedFrom(desc.kind, ArtifactKind::HostCallable) ||
345         isDerivedFrom(desc.kind, ArtifactKind::SharedLibrary)) &&
346        isDerivedFrom(desc.payload, ArtifactPayload::CPULike))
347    {
348        // Get as a file represenation on the OS file system
349        ComPtr<IOSFileArtifactRepresentation> fileRep;
350
351        // We want to keep the file representation, otherwise every request, could produce a new
352        // file and that seems like a bad idea.
353        SLANG_RETURN_ON_FAIL(artifact->requireFile(ArtifactKeep::Yes, fileRep.writeRef()));
354
355        // Try loading the shared library
356        SharedLibrary::Handle handle;
357        if (SLANG_FAILED(SharedLibrary::loadWithPlatformPath(fileRep->getPath(), handle)))
358        {
359            return SLANG_FAIL;
360        }
361
362        // Output
363        *outSharedLibrary = ScopeSharedLibrary::create(handle, fileRep).detach();
364        return SLANG_OK;
365    }
366
367    return SLANG_FAIL;
368}
369
370} // namespace Slang