yum-mirror/slang

Making it easier to work with shaders

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

Yong HeRewriting the lower-buffer-element-type pass to avoid unnecessary packing/unpacking. (#8526)a6deb5ed8

master
50.2 KiB1597 linesraw
1// Stop warnings from Visual Studio
2#define _CRT_SECURE_NO_WARNINGS 1
3
4#include "shader-input-layout.h"
5
6#include "core/slang-token-reader.h"
7#include "core/slang-type-text-util.h"
8
9#include <slang-rhi.h>
10
11namespace renderer_test
12{
13using namespace Slang;
14
15// clang-format off
16#define SLANG_SCALAR_TYPES(x) \
17    x("int", INT32) \
18    x("uint", UINT32) \
19    x("float", FLOAT32)
20// clang-format on
21
22
23Format _getFormatFromName(const UnownedStringSlice& slice)
24{
25    for (int i = 0; i < int(Format::_Count); ++i)
26    {
27        const FormatInfo& info = getFormatInfo(Format(i));
28        if (slice == info.name)
29        {
30            return Format(i);
31        }
32    }
33    return Format::Undefined;
34}
35
36struct TypeInfo
37{
38    UnownedStringSlice name;
39    SlangScalarType type;
40};
41
42#define SLANG_SCALAR_TYPE_INFO(name, value) \
43    {UnownedStringSlice::fromLiteral(name), SLANG_SCALAR_TYPE_##value},
44static const TypeInfo g_scalarTypeInfos[] = {SLANG_SCALAR_TYPES(SLANG_SCALAR_TYPE_INFO)};
45#undef SLANG_SCALAR_TYPES
46#undef SLANG_SCALAR_TYPE_INFO
47
48static SlangScalarType _getScalarType(const UnownedStringSlice& slice)
49{
50    for (const auto& info : g_scalarTypeInfos)
51    {
52        if (info.name == slice)
53        {
54            return info.type;
55        }
56    }
57    return SLANG_SCALAR_TYPE_NONE;
58}
59
60void ShaderInputLayout::AggVal::addField(ShaderInputLayout::Field const& field)
61{
62    fields.add(field);
63}
64
65void ShaderInputLayout::ArrayVal::addField(ShaderInputLayout::Field const& field)
66{
67    vals.add(field.val);
68}
69
70class ShaderInputLayoutFormatException : public Exception
71{
72public:
73    ShaderInputLayoutFormatException(String message)
74        : Exception(message)
75    {
76    }
77};
78
79struct ShaderInputLayoutParser
80{
81    ShaderInputLayout* layout;
82    RandomGenerator* rand;
83
84    ShaderInputLayoutParser(ShaderInputLayout* layout, RandomGenerator* rand)
85        : layout(layout), rand(rand)
86    {
87    }
88
89    RefPtr<ShaderInputLayout::ParentVal> parentVal;
90    List<RefPtr<ShaderInputLayout::ParentVal>> parentValStack;
91
92    SlangResult parseOption(
93        Misc::TokenReader& parser,
94        String const& word,
95        ShaderInputLayout::TextureVal* val)
96    {
97        if (word == "depth")
98        {
99            val->textureDesc.isDepthTexture = true;
100        }
101        else if (word == "arrayLength")
102        {
103            parser.Read("=");
104            val->textureDesc.arrayLength = parser.ReadInt();
105        }
106        else if (word == "size")
107        {
108            parser.Read("=");
109            auto size = parser.ReadInt();
110            val->textureDesc.size = size;
111        }
112        else if (word == "content")
113        {
114            parser.Read("=");
115            auto contentWord = parser.ReadWord();
116            if (contentWord == "zero")
117                val->textureDesc.content = InputTextureContent::Zero;
118            else if (contentWord == "one")
119                val->textureDesc.content = InputTextureContent::One;
120            else if (contentWord == "chessboard")
121                val->textureDesc.content = InputTextureContent::ChessBoard;
122            else
123                val->textureDesc.content = InputTextureContent::Gradient;
124        }
125        else if (word == "sampleCount")
126        {
127            parser.Read("=");
128            auto contentWord = parser.ReadWord();
129            if (contentWord == "one")
130                val->textureDesc.sampleCount = InputTextureSampleCount::One;
131            else if (contentWord == "two")
132                val->textureDesc.sampleCount = InputTextureSampleCount::Two;
133            else if (contentWord == "four")
134                val->textureDesc.sampleCount = InputTextureSampleCount::Four;
135            else if (contentWord == "eight")
136                val->textureDesc.sampleCount = InputTextureSampleCount::Eight;
137            else if (contentWord == "sixteen")
138                val->textureDesc.sampleCount = InputTextureSampleCount::Sixteen;
139            else if (contentWord == "thirtyTwo")
140                val->textureDesc.sampleCount = InputTextureSampleCount::ThirtyTwo;
141            else if (contentWord == "sixtyFour")
142                val->textureDesc.sampleCount = InputTextureSampleCount::SixtyFour;
143        }
144        else if (word == "mipMaps")
145        {
146            parser.Read("=");
147            val->textureDesc.mipMapCount = int(parser.ReadInt());
148        }
149        else if (word == "format")
150        {
151            val->textureDesc.format = parseFormatOption(parser);
152
153            if (val->textureDesc.format == Format::Undefined)
154            {
155                return SLANG_FAIL;
156            }
157        }
158        else
159        {
160            return SLANG_FAIL;
161        }
162        return SLANG_OK;
163    }
164
165    SlangResult parseOption(
166        Misc::TokenReader& parser,
167        String const& word,
168        ShaderInputLayout::SamplerVal* val)
169    {
170        if (word == "depthCompare")
171        {
172            val->samplerDesc.isCompareSampler = true;
173        }
174        else if (word == "filteringMode")
175        {
176            parser.Read("=");
177            auto contentWord = parser.ReadWord();
178            if (contentWord == "point")
179            {
180                val->samplerDesc.filteringMode = TextureFilteringMode::Point;
181            }
182            else
183            {
184                val->samplerDesc.filteringMode = TextureFilteringMode::Linear;
185            }
186        }
187        else
188        {
189            return SLANG_FAIL;
190        }
191        return SLANG_OK;
192    }
193
194
195    SlangResult parseOption(
196        Misc::TokenReader& parser,
197        String const& word,
198        ShaderInputLayout::CombinedTextureSamplerVal* val)
199    {
200        auto result = parseOption(parser, word, val->textureVal);
201        if (SLANG_SUCCEEDED(result))
202            return result;
203
204        result = parseOption(parser, word, val->samplerVal);
205        return result;
206    }
207
208    SlangResult parseOption(
209        Misc::TokenReader& parser,
210        String const& word,
211        ShaderInputLayout::DataValBase* val)
212    {
213        if (word == "data")
214        {
215            parser.Read("=");
216
217            parser.Read("[");
218            uint32_t offset = 0;
219            while (!parser.IsEnd() && !parser.LookAhead("]"))
220            {
221                bool negate = false;
222                if (parser.NextToken().Type == Misc::TokenType::OpSub)
223                {
224                    parser.ReadToken();
225                    negate = true;
226                }
227
228                if (parser.NextToken().Type == Misc::TokenType::IntLiteral)
229                {
230                    uint32_t value = parser.ReadUInt();
231                    if (negate)
232                        value = uint32_t(-int32_t(value));
233                    val->bufferData.add(value);
234                }
235                else
236                {
237                    auto floatNum = parser.ReadFloat();
238                    if (negate)
239                        floatNum = -floatNum;
240                    val->bufferData.add(*(unsigned int*)&floatNum);
241                }
242                offset += 4;
243            }
244            parser.Read("]");
245        }
246        else
247        {
248            return SLANG_FAIL;
249        }
250        return SLANG_OK;
251    }
252
253    SlangResult parseOption(
254        Misc::TokenReader& parser,
255        String const& word,
256        ShaderInputLayout::BufferVal* val)
257    {
258        if (word == "stride")
259        {
260            parser.Read("=");
261            val->bufferDesc.stride = parser.ReadInt();
262        }
263        else if (word == "count")
264        {
265            parser.Read("=");
266            val->bufferDesc.elementCount = parser.ReadInt();
267        }
268        else if (word == "counter")
269        {
270            parser.Read("=");
271            val->bufferDesc.counter = parser.ReadInt();
272        }
273        else if (word == "random")
274        {
275            parser.Read("(");
276            // Read the type
277            String type = parser.ReadWord();
278            SlangScalarType scalarType = _getScalarType(type.getUnownedSlice());
279            if (scalarType == SLANG_SCALAR_TYPE_NONE)
280            {
281                StringBuilder scalarTypeNames;
282                for (const auto& info : g_scalarTypeInfos)
283                {
284                    if (scalarTypeNames.getLength() != 0)
285                    {
286                        scalarTypeNames << ", ";
287                    }
288                    scalarTypeNames << info.name;
289                }
290
291                throw ShaderInputLayoutFormatException(
292                    StringBuilder()
293                    << "Expecting " << scalarTypeNames << " " << parser.NextToken().Position.Line);
294            }
295
296            parser.Read(",");
297            const int size = int(parser.ReadUInt());
298
299            switch (scalarType)
300            {
301            case SLANG_SCALAR_TYPE_INT32:
302                {
303                    bool hasRange = false;
304
305                    int32_t minValue = -0x7fffffff - 1;
306                    int32_t maxValue = 0x7fffffff;
307
308                    if (parser.LookAhead(","))
309                    {
310                        hasRange = true;
311                        parser.ReadToken();
312                        minValue = parser.ReadInt();
313
314                        if (parser.LookAhead(","))
315                        {
316                            parser.ReadToken();
317                            maxValue = parser.ReadInt();
318                        }
319                    }
320                    SLANG_ASSERT(minValue <= maxValue);
321                    maxValue = (maxValue >= minValue) ? maxValue : minValue;
322
323                    // Generate the data
324                    val->bufferData.setCount(size);
325
326                    int32_t* dst = (int32_t*)val->bufferData.getBuffer();
327                    for (int i = 0; i < size; ++i)
328                    {
329                        dst[i] = hasRange ? rand->nextInt32InRange(minValue, maxValue)
330                                          : rand->nextInt32();
331                    }
332                    break;
333                }
334            case SLANG_SCALAR_TYPE_UINT32:
335                {
336                    bool hasRange = false;
337                    uint32_t minValue = 0;
338                    uint32_t maxValue = 0xffffffff;
339
340                    if (parser.LookAhead(","))
341                    {
342                        parser.ReadToken();
343                        minValue = parser.ReadUInt();
344
345                        hasRange = true;
346
347                        if (parser.LookAhead(","))
348                        {
349                            parser.ReadToken();
350                            maxValue = parser.ReadUInt();
351                        }
352                    }
353
354                    SLANG_ASSERT(minValue <= maxValue);
355                    maxValue = (maxValue >= minValue) ? maxValue : minValue;
356
357                    // Generate the data
358                    val->bufferData.setCount(size);
359
360                    uint32_t* dst = (uint32_t*)val->bufferData.getBuffer();
361                    for (int i = 0; i < size; ++i)
362                    {
363                        dst[i] = hasRange ? rand->nextUInt32InRange(minValue, maxValue)
364                                          : rand->nextUInt32();
365                    }
366
367                    break;
368                }
369            case SLANG_SCALAR_TYPE_FLOAT32:
370                {
371                    float minValue = -1.0f;
372                    float maxValue = 1.0f;
373
374                    if (parser.LookAhead(","))
375                    {
376                        parser.ReadToken();
377                        minValue = parser.ReadFloat();
378
379                        if (parser.LookAhead(","))
380                        {
381                            parser.ReadToken();
382                            maxValue = parser.ReadFloat();
383                        }
384                    }
385
386                    SLANG_ASSERT(minValue <= maxValue);
387                    maxValue = (maxValue >= minValue) ? maxValue : minValue;
388
389                    // Generate the data
390                    val->bufferData.setCount(size);
391
392                    float* dst = (float*)val->bufferData.getBuffer();
393                    for (int i = 0; i < size; ++i)
394                    {
395                        dst[i] = (rand->nextUnitFloat32() * (maxValue - minValue)) + minValue;
396                    }
397                    break;
398                }
399            }
400
401            // Read the range
402
403            parser.Read(")");
404        }
405        else if (word == "format")
406        {
407            val->bufferDesc.format = parseFormatOption(parser);
408        }
409        else
410        {
411            return parseOption(parser, word, static_cast<ShaderInputLayout::DataValBase*>(val));
412        }
413        return SLANG_OK;
414    }
415
416    SlangResult parseOption(
417        Misc::TokenReader& parser,
418        String const& word,
419        ShaderInputLayout::ObjectVal* val)
420    {
421        if (word == "type")
422        {
423            parser.Read("=");
424            val->typeName = parser.ReadWord();
425        }
426        else
427        {
428            return SLANG_FAIL;
429        }
430        return SLANG_OK;
431    }
432
433    Format parseFormatOption(Misc::TokenReader& parser)
434    {
435        parser.Read("=");
436        auto formatWord = parser.ReadWord();
437
438        return _getFormatFromName(formatWord.getUnownedSlice());
439    }
440
441    template<typename T>
442    void maybeParseOptions(Misc::TokenReader& parser, T* val)
443    {
444        // parse options
445        if (parser.LookAhead("("))
446        {
447            parser.Read("(");
448            while (!parser.IsEnd() && !parser.LookAhead(")"))
449            {
450                auto word = parser.ReadWord();
451                if (SLANG_FAILED(parseOption(parser, word, val)))
452                {
453                    throw ShaderInputLayoutFormatException(
454                        String("Unsupported option '") + word + String("' at line ") +
455                        String(parser.NextToken().Position.Line));
456                }
457
458                if (parser.LookAhead(","))
459                    parser.Read(",");
460                else
461                    break;
462            }
463            parser.Read(")");
464        }
465    }
466
467    RefPtr<ShaderInputLayout::Val> parseNumericValExpr(
468        Misc::TokenReader& parser,
469        bool negate = false)
470    {
471        switch (parser.NextToken().Type)
472        {
473        case Misc::TokenType::IntLiteral:
474            {
475                RefPtr<ShaderInputLayout::DataVal> val = new ShaderInputLayout::DataVal;
476
477                uint32_t value = parser.ReadUInt();
478                if (negate)
479                    value = uint32_t(-int32_t(value));
480                val->bufferData.add(value);
481
482                return val;
483            }
484            break;
485
486        case Misc::TokenType::DoubleLiteral:
487            {
488                RefPtr<ShaderInputLayout::DataVal> val = new ShaderInputLayout::DataVal;
489
490                float floatValue = parser.ReadFloat();
491                if (negate)
492                    floatValue = -floatValue;
493
494                uint32_t value = 0;
495                memcpy(&value, &floatValue, sizeof(floatValue));
496                val->bufferData.add(value);
497
498                return val;
499            }
500            break;
501
502        default:
503            throw ShaderInputLayoutFormatException(
504                String("Expected a numeric literal but found '") + parser.NextToken().Content +
505                String("' at line") + String(parser.NextToken().Position.Line));
506        }
507    }
508
509    String parseTypeName(Misc::TokenReader& parser)
510    {
511        String typeName = parser.ReadWord();
512        if (parser.AdvanceIf("<"))
513        {
514            StringBuilder sb;
515            sb << typeName << "<";
516            for (;;)
517            {
518                if (parser.LookAhead(Misc::TokenType::IntLiteral))
519                    sb << parser.ReadInt();
520                else
521                    sb << parseTypeName(parser);
522                if (!parser.AdvanceIf(","))
523                    break;
524                sb << ",";
525            }
526            sb << ">";
527            parser.Read(">");
528            return sb.produceString();
529        }
530        return typeName;
531    }
532
533    RefPtr<ShaderInputLayout::Val> parseValExpr(Misc::TokenReader& parser)
534    {
535        typedef Misc::TokenType TokenType;
536
537        switch (parser.NextToken().Type)
538        {
539        case TokenType::OpSub:
540            {
541                parser.ReadToken();
542                return parseNumericValExpr(parser, true);
543            }
544            break;
545
546        case TokenType::IntLiteral:
547        case TokenType::DoubleLiteral:
548            return parseNumericValExpr(parser);
549
550        case TokenType::LBrace:
551            {
552                // aggregate
553                parser.ReadToken();
554                RefPtr<ShaderInputLayout::AggVal> val = new ShaderInputLayout::AggVal;
555
556                while (!parser.IsEnd() && !parser.LookAhead(TokenType::RBrace))
557                {
558                    ShaderInputLayout::Field field;
559
560                    if (parser.LookAhead(TokenType::Identifier) &&
561                        parser.NextToken(1).Type == TokenType::Colon)
562                    {
563                        field.name = parser.ReadWord();
564                        parser.Read(TokenType::Colon);
565                    }
566
567                    field.val = parseValExpr(parser);
568
569                    val->fields.add(field);
570
571                    if (parser.LookAhead(TokenType::RBrace))
572                        break;
573
574                    parser.Read(TokenType::Comma);
575                }
576                parser.Read(TokenType::RBrace);
577
578
579                return val;
580            }
581            break;
582
583        case TokenType::LBracket:
584            {
585                // array
586                parser.ReadToken();
587                RefPtr<ShaderInputLayout::ArrayVal> val = new ShaderInputLayout::ArrayVal;
588
589                while (!parser.IsEnd() && !parser.LookAhead(TokenType::RBracket))
590                {
591                    val->vals.add(parseValExpr(parser));
592
593                    if (parser.LookAhead(TokenType::RBracket))
594                        break;
595
596                    parser.Read(TokenType::Comma);
597                }
598                parser.Read(TokenType::RBracket);
599
600                return val;
601            }
602            break;
603
604        case TokenType::Identifier:
605            {
606                if (parser.AdvanceIf("new"))
607                {
608                    RefPtr<ShaderInputLayout::ObjectVal> val = new ShaderInputLayout::ObjectVal;
609
610                    if (parser.NextToken().Type == TokenType::Identifier)
611                    {
612                        val->typeName = parseTypeName(parser);
613                    }
614
615                    val->contentVal = parseValExpr(parser);
616                    return val;
617                }
618                else if (parser.AdvanceIf("out"))
619                {
620                    auto val = parseValExpr(parser);
621                    val->isOutput = true;
622                    return val;
623                }
624                else if (parser.AdvanceIf("specialize"))
625                {
626                    RefPtr<ShaderInputLayout::SpecializeVal> val =
627                        new ShaderInputLayout::SpecializeVal();
628
629                    parser.Read(Misc::TokenType::LParent);
630                    while (!parser.IsEnd() && parser.NextToken().Type != Misc::TokenType::RParent)
631                    {
632                        val->typeArgs.add(parseTypeName(parser));
633                        if (!parser.AdvanceIf(","))
634                            break;
635                    }
636                    parser.Read(Misc::TokenType::RParent);
637                    val->contentVal = parseValExpr(parser);
638                    return val;
639                }
640                else if (parser.AdvanceIf("dynamic"))
641                {
642                    RefPtr<ShaderInputLayout::SpecializeVal> val =
643                        new ShaderInputLayout::SpecializeVal();
644                    val->typeArgs.add("__Dynamic");
645                    val->contentVal = parseValExpr(parser);
646                    return val;
647                }
648                else
649                {
650                    // We assume that any other word is introducing one of the other
651                    // cases for a parse-able value.
652                    return parseVal(parser);
653                }
654            }
655            break;
656
657        default:
658            throw ShaderInputLayoutFormatException(
659                String("Unexpected '") + parser.NextToken().Content + String("' at line ") +
660                String(parser.NextToken().Position.Line));
661        }
662    }
663
664    RefPtr<ShaderInputLayout::Val> parseVal(Misc::TokenReader& parser)
665    {
666        auto nextToken = parser.NextToken();
667        auto word = nextToken.Content;
668        if (parser.AdvanceIf("begin_array"))
669        {
670            RefPtr<ShaderInputLayout::ArrayVal> val = new ShaderInputLayout::ArrayVal;
671            pushParentVal(val);
672            return val;
673        }
674        else if (parser.AdvanceIf("begin_object"))
675        {
676            RefPtr<ShaderInputLayout::ObjectVal> val = new ShaderInputLayout::ObjectVal;
677            maybeParseOptions(parser, val.Ptr());
678
679            RefPtr<ShaderInputLayout::AggVal> contentVal = new ShaderInputLayout::AggVal;
680            val->contentVal = contentVal;
681            pushParentVal(contentVal);
682
683            return val;
684        }
685        else if (parser.AdvanceIf("uniform"))
686        {
687            RefPtr<ShaderInputLayout::DataVal> val = new ShaderInputLayout::DataVal;
688            maybeParseOptions(parser, val.Ptr());
689            return val;
690        }
691        else if (parser.AdvanceIf("cbuffer"))
692        {
693            // A `cbuffer` is basically just an object where the content of
694            // the object is being provided by `uniform` data instead.
695
696            RefPtr<ShaderInputLayout::ObjectVal> objVal = new ShaderInputLayout::ObjectVal;
697
698            RefPtr<ShaderInputLayout::DataVal> dataVal = new ShaderInputLayout::DataVal;
699            maybeParseOptions(parser, dataVal.Ptr());
700
701            objVal->contentVal = dataVal;
702
703            return objVal;
704        }
705        else if (parser.AdvanceIf("ubuffer"))
706        {
707            RefPtr<ShaderInputLayout::BufferVal> val = new ShaderInputLayout::BufferVal;
708            val->bufferDesc.type = InputBufferType::StorageBuffer;
709            maybeParseOptions(parser, val.Ptr());
710            return val;
711        }
712        else if (parser.AdvanceIf("Texture1D"))
713        {
714            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
715            val->textureDesc.dimension = 1;
716            maybeParseOptions(parser, val.Ptr());
717            return val;
718        }
719        else if (parser.AdvanceIf("RWTextureBuffer"))
720        {
721            RefPtr<ShaderInputLayout::BufferVal> val = new ShaderInputLayout::BufferVal;
722            val->bufferDesc.type = InputBufferType::StorageBuffer;
723            maybeParseOptions(parser, val.Ptr());
724            return val;
725        }
726        else if (parser.AdvanceIf("Texture2D"))
727        {
728            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
729            val->textureDesc.dimension = 2;
730            maybeParseOptions(parser, val.Ptr());
731            return val;
732        }
733        else if (parser.AdvanceIf("Texture3D"))
734        {
735            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
736            val->textureDesc.dimension = 3;
737            maybeParseOptions(parser, val.Ptr());
738            return val;
739        }
740        else if (parser.AdvanceIf("TextureCube"))
741        {
742            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
743            val->textureDesc.dimension = 2;
744            val->textureDesc.isCube = true;
745            maybeParseOptions(parser, val.Ptr());
746            return val;
747        }
748        else if (parser.AdvanceIf("RWTexture1D"))
749        {
750            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
751            val->textureDesc.dimension = 1;
752            val->textureDesc.isRWTexture = true;
753            maybeParseOptions(parser, val.Ptr());
754            return val;
755        }
756        else if (parser.AdvanceIf("RWTexture2D"))
757        {
758            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
759            val->textureDesc.dimension = 2;
760            val->textureDesc.isRWTexture = true;
761            maybeParseOptions(parser, val.Ptr());
762            return val;
763        }
764        else if (parser.AdvanceIf("RWTexture3D"))
765        {
766            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
767            val->textureDesc.dimension = 3;
768            val->textureDesc.isRWTexture = true;
769            maybeParseOptions(parser, val.Ptr());
770            return val;
771        }
772        else if (parser.AdvanceIf("RWTextureCube"))
773        {
774            RefPtr<ShaderInputLayout::TextureVal> val = new ShaderInputLayout::TextureVal;
775            val->textureDesc.dimension = 2;
776            val->textureDesc.isCube = true;
777            val->textureDesc.isRWTexture = true;
778            maybeParseOptions(parser, val.Ptr());
779            return val;
780        }
781        else if (parser.AdvanceIf("Sampler"))
782        {
783            RefPtr<ShaderInputLayout::SamplerVal> val = new ShaderInputLayout::SamplerVal;
784            maybeParseOptions(parser, val.Ptr());
785            return val;
786        }
787        else if (parser.AdvanceIf("TextureSampler1D"))
788        {
789            RefPtr<ShaderInputLayout::CombinedTextureSamplerVal> val =
790                new ShaderInputLayout::CombinedTextureSamplerVal;
791            val->textureVal = new ShaderInputLayout::TextureVal;
792            val->samplerVal = new ShaderInputLayout::SamplerVal;
793            val->textureVal->textureDesc.dimension = 1;
794            maybeParseOptions(parser, val.Ptr());
795            return val;
796        }
797        else if (parser.AdvanceIf("TextureSampler2D"))
798        {
799            RefPtr<ShaderInputLayout::CombinedTextureSamplerVal> val =
800                new ShaderInputLayout::CombinedTextureSamplerVal;
801            val->textureVal = new ShaderInputLayout::TextureVal;
802            val->samplerVal = new ShaderInputLayout::SamplerVal;
803            val->textureVal->textureDesc.dimension = 2;
804            maybeParseOptions(parser, val.Ptr());
805            return val;
806        }
807        else if (parser.AdvanceIf("TextureSampler3D"))
808        {
809            RefPtr<ShaderInputLayout::CombinedTextureSamplerVal> val =
810                new ShaderInputLayout::CombinedTextureSamplerVal;
811            val->textureVal = new ShaderInputLayout::TextureVal;
812            val->samplerVal = new ShaderInputLayout::SamplerVal;
813            val->textureVal->textureDesc.dimension = 3;
814            maybeParseOptions(parser, val.Ptr());
815            return val;
816        }
817        else if (parser.AdvanceIf("TextureSamplerCube"))
818        {
819            RefPtr<ShaderInputLayout::CombinedTextureSamplerVal> val =
820                new ShaderInputLayout::CombinedTextureSamplerVal;
821            val->textureVal = new ShaderInputLayout::TextureVal;
822            val->samplerVal = new ShaderInputLayout::SamplerVal;
823            val->textureVal->textureDesc.dimension = 2;
824            val->textureVal->textureDesc.isCube = true;
825            maybeParseOptions(parser, val.Ptr());
826            return val;
827        }
828        else if (parser.AdvanceIf("AccelerationStructure"))
829        {
830            RefPtr<ShaderInputLayout::AccelerationStructureVal> val =
831                new ShaderInputLayout::AccelerationStructureVal();
832            return val;
833        }
834        else
835        {
836            throw ShaderInputLayoutFormatException(
837                String("Unknown shader input type '") + word + String("' at line: ") +
838                String(nextToken.Position.Line));
839        }
840        parser.ReadToken();
841        return nullptr;
842    }
843
844    String parseName(Misc::TokenReader& parser)
845    {
846        typedef Misc::Token Token;
847        typedef Misc::TokenType TokenType;
848
849        StringBuilder builder;
850
851        Token nameToken = parser.ReadToken();
852        if (nameToken.Type != TokenType::Identifier)
853        {
854            throw ShaderInputLayoutFormatException(
855                StringBuilder() << "Invalid input syntax at line "
856                                << parser.NextToken().Position.Line);
857        }
858        builder << nameToken.Content;
859
860        for (;;)
861        {
862            Token token = parser.NextToken(0);
863
864            if (token.Type == TokenType::LBracket)
865            {
866                parser.ReadToken();
867                int index = parser.ReadInt();
868                SLANG_ASSERT(index >= 0);
869                parser.ReadMatchingToken(TokenType::RBracket);
870
871                builder << "[" << index << "]";
872            }
873            else if (token.Type == TokenType::Dot)
874            {
875                parser.ReadToken();
876                Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier);
877
878                builder << "." << identifierToken.Content;
879            }
880            else
881            {
882                return builder;
883            }
884        }
885    }
886
887    void parseFieldBindings(Misc::TokenReader& parser, ShaderInputLayout::Field& ioField)
888    {
889        // parse bindings
890        if (parser.LookAhead(":"))
891        {
892            parser.Read(":");
893            while (!parser.IsEnd())
894            {
895                if (parser.AdvanceIf("out"))
896                {
897                    ioField.val->isOutput = true;
898                }
899                else if (parser.AdvanceIf("name"))
900                {
901                    // Optionally consume '='
902                    if (parser.NextToken().Type == Misc::TokenType::OpAssign)
903                    {
904                        parser.ReadToken();
905                    }
906
907                    ioField.name = parseName(parser);
908                }
909                else
910                {
911                    fprintf(
912                        stderr,
913                        "Invalid TEST_INPUT syntax '%s'\n",
914                        parser.NextToken().Content.getBuffer());
915                    break;
916                }
917
918                if (parser.LookAhead(","))
919                    parser.Read(",");
920            }
921        }
922    }
923
924    void pushParentVal(ShaderInputLayout::ParentVal* val)
925    {
926        parentValStack.add(parentVal);
927        parentVal = val;
928    }
929
930    void parseValEntry(Misc::TokenReader& parser)
931    {
932        auto parentForNewVal = parentVal;
933
934        ShaderInputLayout::Field field;
935        field.val = parseVal(parser);
936        parseFieldBindings(parser, field);
937
938        parentForNewVal->addField(field);
939    }
940
941    void parseSetEntry(Misc::TokenReader& parser)
942    {
943        auto parentForNewVal = parentVal;
944
945        ShaderInputLayout::Field field;
946        field.name = parseName(parser);
947        parser.Read(Misc::TokenType::OpAssign);
948        field.val = parseValExpr(parser);
949
950        parentForNewVal->addField(field);
951    }
952
953    void parseTypeConformance(Misc::TokenReader& parser)
954    {
955        ShaderInputLayout::TypeConformanceVal conformance;
956        conformance.derivedTypeName = parseTypeName(parser);
957        parser.Read(":");
958        conformance.baseTypeName = parseTypeName(parser);
959        if (parser.AdvanceIf("="))
960            conformance.idOverride = parser.ReadInt();
961        layout->typeConformances.add(conformance);
962    }
963
964    void parseLine(Misc::TokenReader& parser)
965    {
966        if (parser.LookAhead("entryPointSpecializationArg") || parser.LookAhead("type") ||
967            parser.LookAhead("entryPointExistentialType"))
968        {
969            parser.ReadToken();
970            StringBuilder typeExp;
971            while (!parser.IsEnd())
972                typeExp << parser.ReadToken().Content;
973            layout->entryPointSpecializationArgs.add(typeExp);
974        }
975        else if (
976            parser.LookAhead("globalSpecializationArg") || parser.LookAhead("global_type") ||
977            parser.LookAhead("globalExistentialType"))
978        {
979            parser.ReadToken();
980            StringBuilder typeExp;
981            while (!parser.IsEnd())
982                typeExp << parser.ReadToken().Content;
983            layout->globalSpecializationArgs.add(typeExp);
984        }
985        else if (parser.AdvanceIf("render_targets"))
986        {
987            layout->numRenderTargets = parser.ReadInt();
988        }
989        else if (parser.AdvanceIf("end"))
990        {
991            parentVal = parentValStack.getLast();
992            parentValStack.removeLast();
993        }
994        else if (parser.AdvanceIf("set"))
995        {
996            parseSetEntry(parser);
997        }
998        else if (parser.AdvanceIf("type_conformance"))
999        {
1000            parseTypeConformance(parser);
1001        }
1002        else
1003        {
1004            parseValEntry(parser);
1005        }
1006    }
1007
1008    RefPtr<ShaderInputLayout::AggVal> parse(const char* source)
1009    {
1010        RefPtr<ShaderInputLayout::AggVal> rootVal = new ShaderInputLayout::AggVal;
1011        parentVal = rootVal;
1012
1013        auto lines = Misc::Split(source, '\n');
1014        int lineNum = 0;
1015        for (auto& line : lines)
1016        {
1017            lineNum++;
1018            if (!line.startsWith("//"))
1019                continue;
1020            line = line.getUnownedSlice().tail(2).trim();
1021            if (line.startsWith("TEST_INPUT:"))
1022            {
1023                auto lineContent = line.subString(11, line.getLength() - 11);
1024                Misc::TokenReader parser(lineContent);
1025                try
1026                {
1027                    parseLine(parser);
1028                }
1029                catch (const Misc::TextFormatException&)
1030                {
1031                    StringBuilder msg;
1032                    msg << "Invalid input syntax at line " << lineNum << ": " << line;
1033                    throw ShaderInputLayoutFormatException(msg);
1034                }
1035            }
1036        }
1037
1038        // TODO: check that stack has been maintained correctly...
1039
1040        return rootVal;
1041    }
1042};
1043
1044void ShaderInputLayout::parse(RandomGenerator* rand, const char* source)
1045{
1046    rootVal = nullptr;
1047    globalSpecializationArgs.clear();
1048    entryPointSpecializationArgs.clear();
1049
1050    ShaderInputLayoutParser parser(this, rand);
1051    rootVal = parser.parse(source);
1052}
1053
1054/* static */ SlangResult ShaderInputLayout::writeBinding(
1055    slang::TypeLayoutReflection* typeLayout,
1056    const void* data,
1057    size_t sizeInBytes,
1058    WriterHelper writer)
1059{
1060    typedef slang::TypeReflection::ScalarType ScalarType;
1061
1062    slang::TypeReflection::ScalarType scalarType = slang::TypeReflection::ScalarType::None;
1063
1064    slang::TypeLayoutReflection* elementTypeLayout = nullptr;
1065
1066    if (typeLayout)
1067    {
1068        switch (typeLayout->getKind())
1069        {
1070
1071        // case slang::TypeReflection::Kind::Struct:
1072        case slang::TypeReflection::Kind::Array:
1073        case slang::TypeReflection::Kind::Matrix:
1074        case slang::TypeReflection::Kind::Vector:
1075            {
1076                elementTypeLayout = typeLayout->getElementTypeLayout();
1077                break;
1078            }
1079        case slang::TypeReflection::Kind::Scalar:
1080            {
1081                elementTypeLayout = typeLayout;
1082                break;
1083            }
1084        case slang::TypeReflection::Kind::Resource:
1085            {
1086                elementTypeLayout = typeLayout->getElementTypeLayout();
1087                break;
1088            }
1089        case slang::TypeReflection::Kind::TextureBuffer:
1090        case slang::TypeReflection::Kind::ShaderStorageBuffer:
1091            {
1092                elementTypeLayout = typeLayout->getElementTypeLayout();
1093                break;
1094            }
1095        }
1096    }
1097
1098    if (elementTypeLayout)
1099    {
1100        scalarType = elementTypeLayout->getScalarType();
1101    }
1102
1103    if (scalarType != ScalarType::None && scalarType != ScalarType::Void)
1104    {
1105        UnownedStringSlice text = TypeTextUtil::getScalarTypeName(scalarType);
1106        // Write out the type
1107        writer.put("type: ");
1108        writer.put(text);
1109        writer.put("\n");
1110    }
1111
1112    switch (scalarType)
1113    {
1114    // TODO(JS):
1115    // Bool is here, because it's not clear across APIs how bool is laid out in memory
1116    default:
1117    case ScalarType::None:
1118    case ScalarType::Void:
1119    case ScalarType::Bool:
1120        {
1121            auto ptr = (const uint32_t*)data;
1122            const size_t size = sizeInBytes / sizeof(ptr[0]);
1123            for (size_t i = 0; i < size; ++i)
1124            {
1125                uint32_t v = ptr[i];
1126                writer.print("%X\n", v);
1127            }
1128            break;
1129        }
1130    case ScalarType::Float16:
1131        {
1132            auto ptr = (const uint16_t*)data;
1133            const size_t size = sizeInBytes / sizeof(ptr[0]);
1134            for (size_t i = 0; i < size; ++i)
1135            {
1136                const float v = HalfToFloat(ptr[i]);
1137                writer.print("%f\n", v);
1138            }
1139            break;
1140        }
1141    case ScalarType::UInt32:
1142        {
1143            auto ptr = (const uint32_t*)data;
1144            const size_t size = sizeInBytes / sizeof(ptr[0]);
1145            for (size_t i = 0; i < size; ++i)
1146            {
1147                uint32_t v = ptr[i];
1148                writer.print("%u\n", v);
1149            }
1150            break;
1151        }
1152    case ScalarType::Int32:
1153        {
1154            auto ptr = (const int32_t*)data;
1155            const size_t size = sizeInBytes / sizeof(ptr[0]);
1156            for (size_t i = 0; i < size; ++i)
1157            {
1158                int32_t v = ptr[i];
1159                writer.print("%i\n", v);
1160            }
1161            break;
1162        }
1163    case ScalarType::Int64:
1164        {
1165            auto ptr = (const int64_t*)data;
1166            const size_t size = sizeInBytes / sizeof(ptr[0]);
1167            for (size_t i = 0; i < size; ++i)
1168            {
1169                int64_t v = ptr[i];
1170                writer.print("%" PRId64 "\n", v);
1171            }
1172            break;
1173        }
1174    case ScalarType::UInt64:
1175        {
1176            auto ptr = (const uint64_t*)data;
1177            const size_t size = sizeInBytes / sizeof(ptr[0]);
1178            for (size_t i = 0; i < size; ++i)
1179            {
1180                uint64_t v = ptr[i];
1181                writer.print("%" PRIu64 "\n", v);
1182            }
1183            break;
1184        }
1185    case ScalarType::Float32:
1186        {
1187            auto ptr = (const float*)data;
1188            const size_t size = sizeInBytes / sizeof(ptr[0]);
1189            for (size_t i = 0; i < size; ++i)
1190            {
1191                const float v = ptr[i];
1192                writer.print("%f\n", v);
1193            }
1194            break;
1195        }
1196    case ScalarType::Float64:
1197        {
1198            auto ptr = (const double*)data;
1199            const size_t size = sizeInBytes / sizeof(ptr[0]);
1200            for (size_t i = 0; i < size; ++i)
1201            {
1202                const double v = ptr[i];
1203                writer.print("%f\n", v);
1204            }
1205            break;
1206        }
1207    }
1208
1209    return SLANG_OK;
1210}
1211
1212void loadDataIntoHalf(uint16_t& out, const uint8_t& in)
1213{
1214    out = FloatToHalf((float(in) / 255.0f));
1215}
1216void loadDataIntoFloat(float& out, const uint8_t& in)
1217{
1218    out = (float(in) / 255.0f);
1219}
1220template<typename T>
1221void loadDataIntoUint(T& out, const uint8_t& in)
1222{
1223    out = T(in);
1224}
1225template<typename T>
1226void loadDataIntoInt(T& out, const uint8_t& in)
1227{
1228    out = T(in);
1229}
1230
1231// T for type to return, F for function pointer to operate on uint8->T
1232template<typename T, typename F>
1233void generateTextureDataWithTargetTStorage(
1234    TextureData& output,
1235    const InputTextureDesc& desc,
1236    const rhi::FormatInfo& formatInfo,
1237    F loadUint8ToT)
1238{
1239    // the following function assumes input of 0 or 1 since our testing framework only tests with 0
1240    // or 1
1241    TextureData work;
1242    generateTextureDataRGB8(work, desc);
1243
1244    output.init(desc.format);
1245
1246    output.m_textureSize = work.m_textureSize;
1247    output.m_mipLevels = work.m_mipLevels;
1248    output.m_arraySize = work.m_arraySize;
1249
1250    List<TextureData::Slice>& dstSlices = output.m_slices;
1251
1252    Index numSlices = work.m_slices.getCount();
1253    dstSlices.setCount(numSlices);
1254
1255    for (int i = 0; i < numSlices; ++i)
1256    {
1257        const TextureData::Slice& srcSlice = work.m_slices[i];
1258
1259        const Index pixelCount = srcSlice.valuesCount;
1260        const uint8_t* srcPixels = (const uint8_t*)srcSlice.values;
1261
1262        T* dstPixels = (T*)output.setSliceCount(i, pixelCount);
1263        switch (formatInfo.channelCount)
1264        {
1265        case 1:
1266            {
1267                for (Index j = 0; j < pixelCount; ++j, srcPixels += 4, dstPixels += 1)
1268                {
1269                    // Copy out r
1270                    loadUint8ToT(dstPixels[0], srcPixels[0]);
1271                }
1272                break;
1273            }
1274        case 2:
1275            {
1276                for (Index j = 0; j < pixelCount; ++j, srcPixels += 4, dstPixels += 2)
1277                {
1278                    // Copy out rg
1279                    loadUint8ToT(dstPixels[0], srcPixels[0]);
1280                    loadUint8ToT(dstPixels[1], srcPixels[1]);
1281                }
1282                break;
1283            }
1284        case 3:
1285            {
1286                for (Index j = 0; j < pixelCount; ++j, srcPixels += 4, dstPixels += 3)
1287                {
1288                    // Copy out rgb
1289                    loadUint8ToT(dstPixels[0], srcPixels[0]);
1290                    loadUint8ToT(dstPixels[1], srcPixels[1]);
1291                    loadUint8ToT(dstPixels[2], srcPixels[2]);
1292                }
1293                break;
1294            }
1295        case 4:
1296            {
1297                for (Index j = 0; j < pixelCount; ++j, srcPixels += 4, dstPixels += 4)
1298                {
1299                    // Copy out rgba
1300                    loadUint8ToT(dstPixels[0], srcPixels[0]);
1301                    loadUint8ToT(dstPixels[1], srcPixels[1]);
1302                    loadUint8ToT(dstPixels[2], srcPixels[2]);
1303                    loadUint8ToT(dstPixels[3], srcPixels[3]);
1304                }
1305                break;
1306            }
1307        }
1308    }
1309}
1310void generateTextureData(TextureData& output, const InputTextureDesc& desc)
1311{
1312    const FormatInfo& formatInfo = getFormatInfo(desc.format);
1313
1314    switch (desc.format)
1315    {
1316    case Format::RGBA8Unorm:
1317        {
1318            generateTextureDataRGB8(output, desc);
1319            break;
1320        }
1321    case Format::R16Float:
1322    case Format::RG16Float:
1323    case Format::RGBA16Float:
1324        {
1325            generateTextureDataWithTargetTStorage<uint16_t>(
1326                output,
1327                desc,
1328                formatInfo,
1329                loadDataIntoHalf);
1330            break;
1331        }
1332    case Format::R64Uint:
1333        {
1334            generateTextureDataWithTargetTStorage<uint64_t>(
1335                output,
1336                desc,
1337                formatInfo,
1338                loadDataIntoUint<uint64_t>);
1339            break;
1340        }
1341    case Format::R32Float:
1342    case Format::RG32Float:
1343    case Format::RGB32Float:
1344    case Format::RGBA32Float:
1345    case Format::D32Float:
1346        {
1347            generateTextureDataWithTargetTStorage<float>(
1348                output,
1349                desc,
1350                formatInfo,
1351                loadDataIntoFloat);
1352            break;
1353        }
1354    case Format::R32Uint:
1355    case Format::RG32Uint:
1356    case Format::RGB32Uint:
1357    case Format::RGBA32Uint:
1358        {
1359            generateTextureDataWithTargetTStorage<uint32_t>(
1360                output,
1361                desc,
1362                formatInfo,
1363                loadDataIntoUint<uint32_t>);
1364            break;
1365        }
1366    case Format::R16Uint:
1367    case Format::RG16Uint:
1368    case Format::RGBA16Uint:
1369        {
1370            generateTextureDataWithTargetTStorage<uint16_t>(
1371                output,
1372                desc,
1373                formatInfo,
1374                loadDataIntoUint<uint16_t>);
1375            break;
1376        }
1377    case Format::R8Uint:
1378    case Format::RG8Uint:
1379    case Format::RGBA8Uint:
1380        {
1381            generateTextureDataWithTargetTStorage<uint8_t>(
1382                output,
1383                desc,
1384                formatInfo,
1385                loadDataIntoUint<uint8_t>);
1386            break;
1387        }
1388    case Format::R64Sint:
1389        {
1390            generateTextureDataWithTargetTStorage<int64_t>(
1391                output,
1392                desc,
1393                formatInfo,
1394                loadDataIntoInt<int64_t>);
1395            break;
1396        }
1397    case Format::R32Sint:
1398    case Format::RG32Sint:
1399    case Format::RGB32Sint:
1400    case Format::RGBA32Sint:
1401        {
1402            generateTextureDataWithTargetTStorage<int32_t>(
1403                output,
1404                desc,
1405                formatInfo,
1406                loadDataIntoInt<int32_t>);
1407            break;
1408        }
1409    case Format::R16Sint:
1410    case Format::RG16Sint:
1411    case Format::RGBA16Sint:
1412        {
1413            generateTextureDataWithTargetTStorage<int16_t>(
1414                output,
1415                desc,
1416                formatInfo,
1417                loadDataIntoInt<int16_t>);
1418            break;
1419        }
1420    case Format::R8Sint:
1421    case Format::RG8Sint:
1422    case Format::RGBA8Sint:
1423        {
1424            generateTextureDataWithTargetTStorage<int8_t>(
1425                output,
1426                desc,
1427                formatInfo,
1428                loadDataIntoInt<int8_t>);
1429            break;
1430        }
1431    default:
1432        {
1433            SLANG_ASSERT(!"Unhandled format");
1434            break;
1435        }
1436    }
1437}
1438
1439template<typename F>
1440void _iteratePixels(int dimension, int size, unsigned int* buffer, F f)
1441{
1442    if (dimension == 1)
1443        for (int i = 0; i < size; i++)
1444            buffer[i] = f(i, 0, 0);
1445    else if (dimension == 2)
1446        for (int i = 0; i < size; i++)
1447            for (int j = 0; j < size; j++)
1448                buffer[i * size + j] = f(j, i, 0);
1449    else if (dimension == 3)
1450        for (int i = 0; i < size; i++)
1451            for (int j = 0; j < size; j++)
1452                for (int k = 0; k < size; k++)
1453                    buffer[i * size * size + j * size + k] = f(k, j, i);
1454};
1455
1456void generateTextureDataRGB8(TextureData& output, const InputTextureDesc& inputDesc)
1457{
1458    int arrLen = inputDesc.arrayLength;
1459    if (arrLen == 0)
1460        arrLen = 1;
1461
1462    output.init(Format::RGBA8Unorm);
1463
1464    enum class SimpleScalarType
1465    {
1466        kUint,
1467        kInt,
1468        kFloat,
1469    };
1470    SimpleScalarType type;
1471    const rhi::FormatInfo& formatInfo = getFormatInfo(inputDesc.format);
1472    switch (formatInfo.channelType)
1473    {
1474    case SLANG_SCALAR_TYPE_UINT64:
1475    case SLANG_SCALAR_TYPE_UINT32:
1476    case SLANG_SCALAR_TYPE_UINT16:
1477    case SLANG_SCALAR_TYPE_UINT8:
1478        type = SimpleScalarType::kUint;
1479        break;
1480    case SLANG_SCALAR_TYPE_INT64:
1481    case SLANG_SCALAR_TYPE_INT32:
1482    case SLANG_SCALAR_TYPE_INT16:
1483    case SLANG_SCALAR_TYPE_INT8:
1484        type = SimpleScalarType::kInt;
1485        break;
1486    case SLANG_SCALAR_TYPE_FLOAT64:
1487    case SLANG_SCALAR_TYPE_FLOAT32:
1488    case SLANG_SCALAR_TYPE_FLOAT16:
1489        type = SimpleScalarType::kFloat;
1490        break;
1491    default:
1492        type = SimpleScalarType::kUint;
1493        break;
1494    }
1495    // List<List<unsigned int>>& dataBuffer = output.dataBuffer;
1496    int arraySize = arrLen;
1497    if (inputDesc.isCube)
1498        arraySize *= 6;
1499    output.m_arraySize = arraySize;
1500    output.m_textureSize = inputDesc.size;
1501
1502    const Index maxMipLevels = Math::Log2Floor(output.m_textureSize) + 1;
1503    Index mipLevels = (inputDesc.mipMapCount <= 0) ? maxMipLevels : inputDesc.mipMapCount;
1504    mipLevels = (mipLevels > maxMipLevels) ? maxMipLevels : mipLevels;
1505
1506    output.m_mipLevels = int(mipLevels);
1507    output.m_slices.setCount(output.m_mipLevels * output.m_arraySize);
1508
1509    int slice = 0;
1510    for (int i = 0; i < arraySize; i++)
1511    {
1512        for (int j = 0; j < output.m_mipLevels; j++)
1513        {
1514            int size = output.m_textureSize >> j;
1515            int bufferLen = size;
1516            if (inputDesc.dimension == 2)
1517                bufferLen *= size;
1518            else if (inputDesc.dimension == 3)
1519                bufferLen *= size * size;
1520
1521            uint32_t* dst = (uint32_t*)output.setSliceCount(slice, bufferLen);
1522
1523            if (type == SimpleScalarType::kFloat)
1524                _iteratePixels(
1525                    inputDesc.dimension,
1526                    size,
1527                    dst,
1528                    [&](int x, int y, int z) -> unsigned int
1529                    {
1530                        if (inputDesc.content == InputTextureContent::Zero)
1531                        {
1532                            return 0x0;
1533                        }
1534                        else if (inputDesc.content == InputTextureContent::One)
1535                        {
1536                            return 0xFFFFFFFF;
1537                        }
1538                        else if (inputDesc.content == InputTextureContent::Gradient)
1539                        {
1540                            unsigned char r = (unsigned char)(x / (float)(size - 1) * 255.0f);
1541                            unsigned char g = (unsigned char)(y / (float)(size - 1) * 255.0f);
1542                            unsigned char b = (unsigned char)(z / (float)(size - 1) * 255.0f);
1543                            return 0xFF000000 + r + (g << 8) + (b << 16);
1544                        }
1545                        else if (inputDesc.content == InputTextureContent::ChessBoard)
1546                        {
1547                            unsigned int xSig = x < (size >> 1) ? 1 : 0;
1548                            unsigned int ySig = y < (size >> 1) ? 1 : 0;
1549                            unsigned int zSig = z < (size >> 1) ? 1 : 0;
1550                            auto sig = xSig ^ ySig ^ zSig;
1551                            if (sig)
1552                                return 0xFFFFFFFF;
1553                            else
1554                                return 0xFF808080;
1555                        }
1556                        return 0x0;
1557                    });
1558            else if (type == SimpleScalarType::kUint || type == SimpleScalarType::kInt)
1559                _iteratePixels(
1560                    inputDesc.dimension,
1561                    size,
1562                    dst,
1563                    [&](int x, int y, int z) -> unsigned int
1564                    {
1565                        if (inputDesc.content == InputTextureContent::Zero)
1566                        {
1567                            return 0x0;
1568                        }
1569                        else if (inputDesc.content == InputTextureContent::One)
1570                        {
1571                            return 0x01010101;
1572                        }
1573                        else if (inputDesc.content == InputTextureContent::Gradient)
1574                        {
1575                            unsigned char r = (unsigned char)(x / (float)(size - 1));
1576                            unsigned char g = (unsigned char)(y / (float)(size - 1));
1577                            unsigned char b = (unsigned char)(z / (float)(size - 1));
1578                            return 0x01000000 + r + (g << 8) + (b << 16);
1579                        }
1580                        else if (inputDesc.content == InputTextureContent::ChessBoard)
1581                        {
1582                            unsigned int xSig = x < (size >> 1) ? 1 : 0;
1583                            unsigned int ySig = y < (size >> 1) ? 1 : 0;
1584                            unsigned int zSig = z < (size >> 1) ? 1 : 0;
1585                            auto sig = xSig ^ ySig ^ zSig;
1586                            if (sig)
1587                                return 0x01010101;
1588                            else
1589                                return 0x0;
1590                        }
1591                        return 0x0;
1592                    });
1593            slice++;
1594        }
1595    }
1596}
1597} // namespace renderer_test