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