yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1#include "slang-source-embed-util.h" 2 3// Artifact 4#include "../compiler-core/slang-artifact-desc-util.h" 5#include "../compiler-core/slang-artifact-util.h" 6#include "../core/slang-blob.h" 7#include "../core/slang-char-util.h" 8#include "../core/slang-io.h" 9#include "../core/slang-string-escape-util.h" 10#include "../core/slang-string-util.h" 11 12namespace Slang 13{ 14 15namespace 16{// anonymous 17typedef SourceEmbedUtil ::Style Style ; 18}// namespace 19 20static const NamesDescriptionValue kSourceEmbedStyleInfos []= { 21 {ValueInt (Style ::None ),"none" ,"No source level embedding" }, 22 {ValueInt (Style ::Default ),"default" ,"The default embedding for the type to be embedded" }, 23 {ValueInt (Style ::Text ), 24"text" , 25"Embed as text. May change line endings. If output isn't text will use 'default'. Size will " 26"*not* contain terminating 0." }, 27 {ValueInt (Style ::BinaryText ),"binary-text" ,"Embed as text assuming contents is binary. " }, 28 {ValueInt (Style ::U8 ),"u8" ,"Embed as unsigned bytes." }, 29 {ValueInt (Style ::U16 ),"u16" ,"Embed as uint16_t." }, 30 {ValueInt (Style ::U32 ),"u32" ,"Embed as uint32_t." }, 31 {ValueInt (Style ::U64 ),"u64" ,"Embed as uint64_t." }, 32}; 33 34/* static */ ConstArrayView < NamesDescriptionValue > SourceEmbedUtil ::getStyleInfos () 35{ 36return makeConstArrayView (kSourceEmbedStyleInfos ); 37} 38 39/* static */ bool SourceEmbedUtil ::isSupported (SlangSourceLanguage lang ) 40{ 41return lang == SLANG_SOURCE_LANGUAGE_CPP || lang == SLANG_SOURCE_LANGUAGE_C ; 42} 43 44static bool _isHeaderExtension (const UnownedStringSlice & in ) 45{ 46// Some "typical" header extensions 47return in == toSlice ("h" )|| in == toSlice ("hpp" )|| in == toSlice ("hxx" )|| 48in == toSlice ("h++" )|| in == toSlice ("hh" ); 49} 50 51/* static */ String SourceEmbedUtil ::getPath (const String & path ,const Options & options ) 52{ 53if (!isSupported (options .language )) 54 { 55return String (); 56 } 57 58if (!path .getLength ()) 59 { 60return path ; 61 } 62 63const auto ext = Path ::getPathExt (path ); 64 65if (_isHeaderExtension (ext .getUnownedSlice ())) 66 { 67return path ; 68 } 69 70// Assume it's a header, and just use the .h extension 71StringBuilder buf ; 72buf <<path <<toSlice (".h" ); 73return buf ; 74} 75 76/* static */ SourceEmbedUtil ::Style SourceEmbedUtil ::getDefaultStyle (const ArtifactDesc & desc ) 77{ 78if (ArtifactDescUtil ::isText (desc )) 79 { 80return Style ::Text ; 81 } 82 83if (isDerivedFrom (desc .kind ,ArtifactKind ::CompileBinary )) 84 { 85// SPIR-V is encoded as U32 86if (isDerivedFrom (desc .payload ,ArtifactPayload ::SPIRV )) 87 { 88return Style ::U32 ; 89 } 90 } 91 92// When in doube encode as U8 bytes. 93// The problem is on some compilers there are limits on how long a U8 based binary can be. 94return Style ::U8 ; 95} 96 97// True if we need to copy into a buffer. Necessary if there is an alignement 98// issue or if there is a partial entry 99static bool _needsCopy (const uint8_t * cur ,Count bytesPerElement ,Count bytesPerLine ) 100{ 101return ((size_t (bytesPerLine ) |size_t (cur ))& size_t (bytesPerElement - 1 ))!= 0 ; 102} 103 104// NOTE! Assumes T is an unsigned type. Behavior will be incorrect if it is not. 105template < typename T > 106static void _appendHex ( 107const T * in , 108ArrayView < char > elementWork , 109char * dst , 110size_t bytesForLine , 111StringBuilder & out ) 112{ 113// Check that T is unsigned 114SLANG_COMPILE_TIME_ASSERT ((T (~T (0 )))> T (0 )); 115 116// Make sure dst seems plausible 117SLANG_ASSERT (dst >=elementWork .begin ()&& dst <=elementWork .end ()); 118// Check the alignment 119SLANG_ASSERT ((size_t (in )& (sizeof (T )- 1 ))== 0 ); 120 121// Calculate the amount of elements for this line. 122const size_t elementsCount = (bytesForLine + sizeof (T )- 1 ) /sizeof (T ); 123 124// The amount of hex digits needed, is 2 per byte 125const Count numHexDigits = sizeof (T )* 2 ; 126 127// Shift to get top nybble 128const Index shift = (numHexDigits - 1 )* 4 ; 129 130for (size_t i = 0 ;i < elementsCount ;++ i ) 131 { 132T value = in [i ]; 133 134for (Index j = 0 ;j < numHexDigits ;j ++ ,value <<=4 ) 135 { 136dst [j ]= CharUtil ::getHexChar (Index (value >>shift )& 0xf ); 137 } 138 139out .append (elementWork .getBuffer (),elementWork .getCount ()); 140 } 141} 142 143static SlangResult _append ( 144const SourceEmbedUtil ::Options & options , 145ConstArrayView < uint8_t > data , 146StringBuilder & buf ) 147{ 148const uint8_t * cur = data .begin (); 149 150const auto prefix = toSlice ("0x" ); 151const auto suffix = toSlice (", " ); 152UnownedStringSlice literalSuffix ; 153 154UnownedStringSlice elementType ; 155 156Count bytesPerElement ; 157 158switch (options .style ) 159 { 160case Style ::U8 : 161 { 162elementType = toSlice ("unsigned char" ); 163bytesPerElement = 1 ; 164break ; 165 } 166case Style ::U16 : 167 { 168elementType = toSlice ("uint16_t" ); 169bytesPerElement = 2 ; 170break ; 171 } 172case Style ::U32 : 173 { 174elementType = toSlice ("uint32_t" ); 175bytesPerElement = 4 ; 176break ; 177 } 178case Style ::U64 : 179 { 180elementType = toSlice ("uint64_t" ); 181bytesPerElement = 8 ; 182// On testing on GCC/CLANG/Recent VS, there is no warning/error without suffix, so 183// will leave off for now. 184// literalSuffix = toSlice("ULL"); 185break ; 186 } 187default : 188return SLANG_FAIL ; 189 } 190 191// Output the variable 192 193buf <<"const " <<elementType <<" " <<options .variableName <<"[] = \n" ; 194buf <<"{\n" ; 195 196// Work out the element work 197char work [80 ]; 198Count elementSizeInChars ; 199 { 200StringBuilder workBuf ; 201workBuf <<prefix ; 202workBuf .appendRepeatedChar ('N' ,2 * bytesPerElement ); 203workBuf <<literalSuffix ; 204workBuf <<suffix ; 205 206elementSizeInChars = workBuf .getLength (); 207 ::memcpy (work ,workBuf .getBuffer (),elementSizeInChars ); 208 } 209 210auto workView = makeArrayView (work ,elementSizeInChars ); 211char * dstChars = work + prefix .getLength (); 212 213Count elementsPerLine = (options .lineLength - options .indent .getLength ()) /elementSizeInChars ; 214elementsPerLine = (elementsPerLine <=0 ) ?1 :elementsPerLine ; 215 216// Maximum bytes output per line 217const size_t bytesPerLine = elementsPerLine * bytesPerElement ; 218 219List < uint64_t > alignedElements ; 220alignedElements .setCount (Count ((bytesPerLine /sizeof (uint64_t ))+ 2 )); 221uint8_t * alignedDst = (uint8_t * )alignedElements .getBuffer (); 222 223size_t bytesRemaining = data .getCount (); 224 225while (bytesRemaining > 0 ) 226 { 227const size_t bytesForLine = bytesRemaining > bytesPerLine ?bytesPerLine :bytesRemaining ; 228bytesRemaining -= bytesForLine ; 229 230const uint8_t * lineBytes = cur ; 231cur += bytesForLine ; 232 233// We copy if we want alignment of if we hit a partial at the end 234if (_needsCopy (lineBytes ,bytesPerElement ,bytesForLine )) 235 { 236// Make sure the last element is zeroed, before copying 237// Needed if the last element is partial. 238alignedElements [Index (bytesForLine /sizeof (uint64_t ))]= 0 ; 239 240// Copy the bytes over 241 ::memcpy (alignedDst ,lineBytes ,bytesForLine ); 242 243// Use the aligned buffer for the line 244lineBytes = alignedDst ; 245 } 246 247buf <<options .indent ; 248 249switch (bytesPerElement ) 250 { 251case 1 : 252_appendHex < uint8_t > (lineBytes ,workView ,dstChars ,bytesForLine ,buf ); 253break ; 254case 2 : 255_appendHex < uint16_t > ((const uint16_t * )lineBytes ,workView ,dstChars ,bytesForLine ,buf ); 256break ; 257case 4 : 258_appendHex < uint32_t > ((const uint32_t * )lineBytes ,workView ,dstChars ,bytesForLine ,buf ); 259break ; 260case 8 : 261_appendHex < uint64_t > ((const uint64_t * )lineBytes ,workView ,dstChars ,bytesForLine ,buf ); 262break ; 263 } 264 265buf <<"\n" ; 266 } 267 268buf <<"};\n\n" ; 269 270return SLANG_OK ; 271} 272 273/* static */ SlangResult SourceEmbedUtil ::createEmbedded ( 274IArtifact * artifact , 275const Options & inOptions , 276ComPtr < IArtifact >& outArtifact ) 277{ 278if (!isSupported (inOptions .language )) 279 { 280return SLANG_E_NOT_IMPLEMENTED ; 281 } 282 283ComPtr < ISlangBlob > blob ; 284SLANG_RETURN_ON_FAIL (artifact -> loadBlob (ArtifactKeep ::No ,blob .writeRef ())); 285 286const auto desc = artifact -> getDesc (); 287 288Options options (inOptions ); 289 290// If the style is text, but the artifact *isn't* a text type, we'll 291// use 'default' for the type 292if (options .style == Style ::Text && !ArtifactDescUtil ::isText (desc )) 293 { 294options .style = Style ::Default ; 295 } 296 297if (options .style == Style ::Default ) 298 { 299options .style = getDefaultStyle (desc ); 300 } 301 302// If there is no style there is nothing to do 303if (options .style == Style ::None ) 304 { 305return SLANG_OK ; 306 } 307 308if (options .variableName .getLength () <=0 ) 309 { 310options .variableName = "data" ; 311 } 312 313StringBuilder buf ; 314 315ConstArrayView < uint8_t > data ((const uint8_t * )blob -> getBufferPointer (),blob -> getBufferSize ()); 316 317size_t totalSizeInBytes = data .getCount (); 318 319switch (options .style ) 320 { 321case Style ::Text : 322 { 323totalSizeInBytes = 0 ; 324 325auto handler = StringEscapeUtil ::getHandler (StringEscapeUtil ::Style ::Cpp ); 326 327buf <<"const char " <<options .variableName <<"[] = \n" ; 328 329// Split into lines 330// We dont worry about splitting lines in this impl... 331UnownedStringSlice text ((const char * )data .begin (),data .getCount ()); 332 333for (auto line :LineParser (text )) 334 { 335buf <<options .indent ; 336buf <<"\"" ; 337 338handler -> appendEscaped (line ,buf ); 339 340// Work out the total size, taking into account we may encode line endings and \0 341// differently The +1 is for \n 342totalSizeInBytes += line .getLength ()+ 1 ; 343 344buf <<"\\n\"\n" ; 345 } 346 347buf <<";\n" ; 348break ; 349 } 350case Style ::BinaryText : 351 { 352auto handler = StringEscapeUtil ::getHandler (StringEscapeUtil ::Style ::Cpp ); 353 354buf <<"const char " <<options .variableName <<"[] = \n" ; 355 356// We could encode everything and then split 357// but if we do that we probably want to not split across an escaped character, 358// although that may be handled correctly. 359 360// The other way to this is incrementally, so that's what we will do here 361UnownedStringSlice text ((const char * )data .begin (),data .getCount ()); 362 363auto cur = text .begin (); 364auto end = text .end (); 365 366while (cur < end ) 367 { 368const auto startOffset = buf .getLength (); 369 370buf <<options .indent ; 371buf <<"\"" ; 372 373do 374 { 375handler -> appendEscaped (UnownedStringSlice (cur ,1 ),buf ); 376cur ++ ; 377 }while (buf .getLength ()- startOffset < options .lineLength - 1 ); 378 379buf <<"\"\n" ; 380 } 381 382buf <<";\n" ; 383break ; 384 } 385case Style ::U8 : 386case Style ::U16 : 387case Style ::U32 : 388case Style ::U64 : 389 { 390SLANG_RETURN_ON_FAIL (_append (options ,data ,buf )); 391break ; 392 } 393default : 394 { 395return SLANG_E_NOT_IMPLEMENTED ; 396 } 397 } 398 399buf <<"const size_t " <<options .variableName 400 <<"_sizeInBytes = " <<uint64_t (totalSizeInBytes ) <<";\n\n" ; 401 402// Make into an artifact 403ArtifactPayload payload = 404options .language == SLANG_SOURCE_LANGUAGE_C ?ArtifactPayload ::C :ArtifactPayload ::Cpp ; 405auto dstDesc = ArtifactDesc ::make (ArtifactKind ::Source ,payload ); 406 407auto dstArtifact = ArtifactUtil ::createArtifact (dstDesc ); 408 409auto dstBlob = StringBlob ::moveCreate (buf ); 410dstArtifact -> addRepresentationUnknown (dstBlob ); 411 412outArtifact = dstArtifact ; 413return SLANG_OK ; 414} 415 416}// namespace Slang