/////////////////////////////////////////////////////////////////////////////// // // // Objects.cpp // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // // // /////////////////////////////////////////////////////////////////////////////// #include "CompilationResult.h" #include "HLSLTestData.h" #include #include "WexTestClass.h" #include "HlslTestUtils.h" #include #include /////////////////////////////////////////////////////////////////////////////// // Utilities. namespace std { /// Returns the first element of the container. template auto first(Container& cont) -> decltype (*begin(cont)) { auto iter = begin(cont); return *iter; } /// Returns the first element of the container or the default value if empty. template ElementType first_or_default( Container& cont, const ElementType& defaultValue ) { auto iter = begin(cont); auto endIter = end(cont); if (iter == endIter) return defaultValue; return *iter; } } template class EnumFlagsIterator : public std::iterator { private: unsigned long _value; public: EnumFlagsIterator(TEnumeration value) : _value(value) { } TEnumeration operator*() const { unsigned long l; _BitScanForward(&l, _value); return static_cast(1 << l); } EnumFlagsIterator& operator++() { unsigned long l; _BitScanForward(&l, _value); _value = _value & ~(1 << l); return *this; } bool equal(const EnumFlagsIterator& other) { return _value == other._value; } bool operator==(const EnumFlagsIterator& other) { return _value == other._value; } bool operator!=(const EnumFlagsIterator& other) { return _value != other._value; } }; /////////////////////////////////////////////////////////////////////////////// // Shader model test data. enum ShaderModel { ShaderModel4 = 4, ShaderModel5 = 5 }; enum ShaderType { /// Vertex shader. ST_Vertex = 1 << 0, /// Hull shader. ST_Hull = 1 << 1, /// Domain shader. ST_Domain = 1 << 2, /// Geometry shader type. ST_Geometry = 1 << 3, /// Pixel shader type. ST_Pixel = 1 << 4, /// Compute shader type. ST_Compute = 1 << 5, /// Hull and geometry shader types. ST_HullGeometry = ST_Hull | ST_Geometry, /// Hull and domain shader types. ST_HullDomain = ST_Hull | ST_Domain, /// Pixel and compute shader types. ST_PixelCompute = ST_Pixel | ST_Compute, /// All shader types. ST_All = ST_Vertex | ST_Hull | ST_Domain | ST_Geometry | ST_Pixel | ST_Compute }; static const char* GetShaderTypeName(ShaderType value) { switch (value) { case ST_Vertex: return "Vertex shader"; case ST_Hull: return "Hull shader"; case ST_Domain: return "Domain shader"; case ST_Geometry: return "Geometry shader"; case ST_Pixel: return "Pixel shader"; case ST_Compute: return "Compute shader"; default: return "(unknown)"; } } typedef EnumFlagsIterator ShaderTypeIterator; ShaderTypeIterator begin(ShaderType value) { return ShaderTypeIterator(value); } ShaderTypeIterator end(ShaderType value) { return ShaderTypeIterator((ShaderType)0); } /// Enumerates types of shader objects like textures or buffers. enum ShaderObjectKind { /// Output buffer that appears as a stream the shader may append to. SOK_AppendStructuredBuffer, /// SOK_Buffer, SOK_ByteAddressBuffer, /// An input buffer that appears as a stream the shader may pull values from. SOK_ConsumeStructuredBuffer, /// Represents an array of control points that are available to the hull shader as inputs. SOK_InputPatch, /// Represents an array of output control points that are available to the hull shader's patch-constant function as well as the domain shader. SOK_OutputPatch, /// A read/write buffer. SOK_RWBuffer, /// A read/write buffer that indexes in bytes. SOK_RWByteAddressBuffer, /// A read/write buffer that can take a T type that is a structure. SOK_RWStructuredBuffer, /// A read/write resource. SOK_RWTexture1D, /// A read/write resource. SOK_RWTexture1DArray, /// A read/write resource. SOK_RWTexture2D, /// A read/write resource. SOK_RWTexture2DArray, /// A read/write resource. SOK_RWTexture3D, /// A stream-output object is a templated object that streams data out of the geometry-shader stage. SOK_StreamOutputLine, /// A stream-output object is a templated object that streams data out of the geometry-shader stage. SOK_StreamOutputPoint, /// A stream-output object is a templated object that streams data out of the geometry-shader stage. SOK_StreamOutputTriangle, /// A read-only buffer, which can take a T type that is a structure. SOK_StructuredBuffer, /// A read-only resource. SOK_Texture1D, /// A read-only resource. SOK_Texture1DArray, /// A read-only resource. SOK_Texture2D, /// A read-only resource. SOK_Texture2DArray, /// A read-only resource. SOK_Texture2DMS, /// A read-only resource. SOK_Texture2DArrayMS, /// A read-only resource. SOK_Texture3D, /// A read-only resource. SOK_TextureCube, /// A read-only resource. SOK_TextureCubeArray }; static bool IsStreamOutputObject(ShaderObjectKind value) { return (value == SOK_StreamOutputPoint) || (value == SOK_StreamOutputLine) || (value == SOK_StreamOutputTriangle); } struct ShaderObjectDataItem { ShaderObjectKind Kind; const char* TypeName; ShaderModel MinShaderModel; ShaderType ValidShaderTypes; }; static const ShaderObjectDataItem ShaderObjectData[] = { { SOK_AppendStructuredBuffer, "AppendStructuredBuffer", ShaderModel5, ST_PixelCompute }, { SOK_Buffer, "Buffer", ShaderModel5, ST_All }, { SOK_ByteAddressBuffer, "ByteAddressBuffer", ShaderModel5, ST_All }, { SOK_ConsumeStructuredBuffer, "ConsumeStructuredBuffer", ShaderModel5, ST_PixelCompute }, { SOK_InputPatch, "InputPatch", ShaderModel5, ST_HullGeometry }, { SOK_OutputPatch, "OutputPatch", ShaderModel5, ST_HullDomain }, { SOK_RWBuffer, "RWBuffer", ShaderModel5, ST_PixelCompute }, { SOK_RWByteAddressBuffer, "RWByteAddressBuffer", ShaderModel5, ST_PixelCompute }, { SOK_RWStructuredBuffer, "RWStructuredBuffer", ShaderModel5, ST_PixelCompute }, { SOK_RWTexture1D, "RWTexture1D", ShaderModel5, ST_PixelCompute }, { SOK_RWTexture1DArray, "RWTexture1DArray", ShaderModel5, ST_PixelCompute }, { SOK_RWTexture2D, "RWTexture2D", ShaderModel5, ST_PixelCompute }, { SOK_RWTexture2DArray, "RWTexture2DArray", ShaderModel5, ST_PixelCompute }, { SOK_RWTexture3D, "RWTexture3D", ShaderModel5, ST_PixelCompute }, { SOK_StreamOutputLine, "LineStream", ShaderModel4, ST_Geometry }, { SOK_StreamOutputPoint, "PointStream", ShaderModel4, ST_Geometry }, { SOK_StreamOutputTriangle, "TriangleStream", ShaderModel4, ST_Geometry }, { SOK_StructuredBuffer, "StructuredBuffer", ShaderModel5, ST_All }, { SOK_Texture1D, "Texture1D", ShaderModel4, ST_All }, { SOK_Texture1DArray, "Texture1DArray", ShaderModel4, ST_All }, { SOK_Texture2D, "Texture2D", ShaderModel4, ST_All }, { SOK_Texture2DArray, "Texture2DArray", ShaderModel4, ST_All }, { SOK_Texture2DMS, "Texture2DMS", ShaderModel4, ST_All }, { SOK_Texture2DArrayMS, "Texture2DMSArray", ShaderModel4, ST_All }, { SOK_Texture3D, "Texture3D", ShaderModel4, ST_All }, { SOK_TextureCube, "TextureCube", ShaderModel4, ST_All }, { SOK_TextureCubeArray, "TextureCubeArray", ShaderModel4, ST_All } }; /// Enumerates the template shapes. /// This is a crude simplification of what the type system could do, /// but it covers all cases without unused generality. enum ShaderObjectTemplateKind { /// No parameters. SOTK_NoParams, /// Single parameter of type scalar or vector. SOTK_SingleSVParam, /// Single parameter of type scalar, vector or struct. SOTK_SingleSVCParam, /// Two parameters; a scalar or vector type and a sample count. SOTK_SVAndSampleCountParams, /// Two parameters; a scalar or vector type and a control point count (1-32). SOTK_SVCAndControlPointCountParams }; struct ShaderObjectTemplateDataItem { ShaderObjectKind Kind; ShaderObjectTemplateKind TemplateKind; bool TemplateParamsOptional; }; static const bool OptionalTrue = true; static const bool OptionalFalse = false; static const ShaderObjectTemplateDataItem ShaderObjectTemplateData[] = { { SOK_AppendStructuredBuffer, SOTK_SingleSVCParam, OptionalFalse }, { SOK_Buffer, SOTK_SingleSVParam, OptionalFalse }, { SOK_ByteAddressBuffer, SOTK_NoParams, OptionalTrue }, { SOK_ConsumeStructuredBuffer, SOTK_SingleSVCParam, OptionalFalse }, { SOK_InputPatch, SOTK_SVCAndControlPointCountParams, OptionalFalse }, { SOK_OutputPatch, SOTK_SVCAndControlPointCountParams, OptionalFalse }, { SOK_RWBuffer, SOTK_SingleSVParam, OptionalFalse }, { SOK_RWByteAddressBuffer, SOTK_NoParams, OptionalTrue }, { SOK_RWStructuredBuffer, SOTK_SingleSVCParam, OptionalFalse }, { SOK_RWTexture1D, SOTK_SingleSVParam, OptionalFalse }, { SOK_RWTexture1DArray, SOTK_SingleSVParam, OptionalFalse }, { SOK_RWTexture2D, SOTK_SingleSVParam, OptionalFalse }, { SOK_RWTexture2DArray, SOTK_SingleSVParam, OptionalFalse }, { SOK_RWTexture3D, SOTK_SingleSVParam, OptionalFalse }, { SOK_StreamOutputLine, SOTK_SingleSVCParam, OptionalFalse }, { SOK_StreamOutputPoint, SOTK_SingleSVCParam, OptionalFalse }, { SOK_StreamOutputTriangle, SOTK_SingleSVCParam, OptionalFalse }, { SOK_StructuredBuffer, SOTK_SingleSVCParam, OptionalFalse }, { SOK_Texture1D, SOTK_SingleSVParam, OptionalTrue }, { SOK_Texture1DArray, SOTK_SingleSVParam, OptionalTrue }, { SOK_Texture2D, SOTK_SingleSVParam, OptionalTrue }, { SOK_Texture2DArray, SOTK_SingleSVParam, OptionalTrue }, { SOK_Texture2DMS, SOTK_SVAndSampleCountParams, OptionalFalse }, { SOK_Texture2DArrayMS, SOTK_SVAndSampleCountParams, OptionalFalse }, { SOK_Texture3D, SOTK_SingleSVParam, OptionalTrue }, { SOK_TextureCube, SOTK_SingleSVParam, OptionalTrue }, { SOK_TextureCubeArray, SOTK_SingleSVParam, OptionalTrue } }; static const ShaderObjectTemplateDataItem& GetTemplateData(const ShaderObjectDataItem& sod) { static_assert( _countof(ShaderObjectTemplateData) == _countof(ShaderObjectData), "otherwise lookup tables have different elements"); struct Unary { ShaderObjectKind ObjectKind; Unary(ShaderObjectKind ok) : ObjectKind(ok) { } bool operator()(const ShaderObjectTemplateDataItem& i) { return i.Kind == ObjectKind; } }; Unary filter(sod.Kind); auto iter = std::find_if(std::begin(ShaderObjectTemplateData), std::end(ShaderObjectTemplateData), filter); assert(iter != std::end(ShaderObjectTemplateData)); return *iter; } static bool HasOptionalTemplateArguments(const ShaderObjectDataItem& sod) { const ShaderObjectTemplateDataItem& templateData = GetTemplateData(sod); return templateData.TemplateKind != SOTK_NoParams && templateData.TemplateParamsOptional; } static int CountOptionalTemplateArguments(const ShaderObjectTemplateDataItem& templateData) { if (!templateData.TemplateParamsOptional) { return 0; } switch (templateData.TemplateKind) { default: case SOTK_NoParams: return 0; case SOTK_SingleSVParam: return 1; case SOTK_SingleSVCParam: return 1; case SOTK_SVAndSampleCountParams: return 2; case SOTK_SVCAndControlPointCountParams: return 2; } } struct ShaderObjectMethodDataItem { ShaderObjectKind ObjectKind; const char* MethodName; }; static const ShaderObjectDataItem ShaderObjectMethodData[] = { { SOK_AppendStructuredBuffer, "Append" }, { SOK_AppendStructuredBuffer, "GetDimensions" }, { SOK_Buffer, "GetDimensions" }, { SOK_Buffer, "Load" }, // overloaded! { SOK_Buffer, "operator[]" }, { SOK_ByteAddressBuffer, "GetDimensions" }, { SOK_ByteAddressBuffer, "Load" }, // overloaded { SOK_ByteAddressBuffer, "Load2" }, // overloaded { SOK_ByteAddressBuffer, "Load3" }, // overloaded { SOK_ByteAddressBuffer, "Load4" }, // overloaded { SOK_ConsumeStructuredBuffer, "Consume" }, { SOK_ConsumeStructuredBuffer, "GetDimensions" }, { SOK_InputPatch, "operator[]" }, { SOK_InputPatch, "Length" }, // actually a property { SOK_OutputPatch, "operator[]" }, { SOK_OutputPatch, "Length" }, // actually a property { SOK_RWBuffer, "GetDimensions" }, { SOK_RWBuffer, "Load" }, // overloaded! { SOK_RWBuffer, "operator[]" } // TODO: add RWByteAddressBuffer members // TODO: add RWStructuredBuffer members }; // - a RWBuffer supports globallycoherent storage class to generate memory barriers // TODO: ByteAddressBuffer is supported on SM4 on compute shaders // TODO: RWByteAddressBuffer is supported on SM4 on compute shaders // TODO: RWStructuredBuffer is supported on SM4 on compute shaders #include "dxc/dxcapi.internal.h" #include "dxc/HlslIntrinsicOp.h" #include "gen_intrin_main_tables_15.h" struct ShaderObjectIntrinsicDataItem { // Kind of shader object described. ShaderObjectKind Kind; // Pointer to first intrinsic in table. const HLSL_INTRINSIC* Intrinsics; // Count of elements in intrinsic table. size_t IntrinsicCount; }; const ShaderObjectIntrinsicDataItem ShaderObjectIntrinsicData[] = { { SOK_AppendStructuredBuffer, g_AppendStructuredBufferMethods, _countof(g_AppendStructuredBufferMethods) }, { SOK_Buffer, g_BufferMethods, _countof(g_BufferMethods) }, { SOK_ByteAddressBuffer, g_ByteAddressBufferMethods, _countof(g_ByteAddressBufferMethods) }, { SOK_ConsumeStructuredBuffer, g_ConsumeStructuredBufferMethods, _countof(g_ByteAddressBufferMethods) }, { SOK_InputPatch, nullptr, 0 }, { SOK_OutputPatch, nullptr, 0 }, { SOK_RWBuffer, g_RWBufferMethods, _countof(g_RWBufferMethods) }, { SOK_RWByteAddressBuffer, g_RWByteAddressBufferMethods, _countof(g_RWByteAddressBufferMethods) }, { SOK_RWStructuredBuffer, g_RWStructuredBufferMethods, _countof(g_RWStructuredBufferMethods) }, { SOK_RWTexture1D, g_RWTexture1DMethods, _countof(g_RWTexture1DMethods) }, { SOK_RWTexture1DArray, g_RWTexture1DArrayMethods, _countof(g_RWTexture1DArrayMethods) }, { SOK_RWTexture2D, g_RWTexture2DMethods, _countof(g_RWTexture2DMethods) }, { SOK_RWTexture2DArray, g_RWTexture2DArrayMethods, _countof(g_RWTexture2DArrayMethods) }, { SOK_RWTexture3D, g_RWTexture3DMethods, _countof(g_RWTexture3DMethods) }, { SOK_StreamOutputLine, g_StreamMethods, _countof(g_StreamMethods) }, { SOK_StreamOutputPoint, g_StreamMethods, _countof(g_StreamMethods) }, { SOK_StreamOutputTriangle, g_StreamMethods, _countof(g_StreamMethods) }, { SOK_StructuredBuffer, g_StructuredBufferMethods, _countof(g_StructuredBufferMethods) }, { SOK_Texture1D, g_Texture1DMethods, _countof(g_Texture1DMethods) }, { SOK_Texture1DArray, g_Texture1DArrayMethods, _countof(g_Texture1DArrayMethods) }, { SOK_Texture2D, g_Texture2DMethods, _countof(g_Texture2DMethods) }, { SOK_Texture2DArray, g_Texture2DArrayMethods, _countof(g_Texture2DArrayMethods) }, { SOK_Texture2DMS, g_Texture2DMSMethods, _countof(g_Texture2DMSMethods) }, { SOK_Texture2DArrayMS, g_Texture2DArrayMSMethods, _countof(g_Texture2DArrayMSMethods) }, { SOK_Texture3D, g_Texture3DMethods, _countof(g_Texture3DMethods) }, { SOK_TextureCube, g_TextureCUBEMethods, _countof(g_TextureCUBEMethods) }, { SOK_TextureCubeArray, g_TextureCUBEArrayMethods, _countof(g_TextureCUBEArrayMethods) } }; static const ShaderObjectIntrinsicDataItem& GetIntrinsicData(const ShaderObjectDataItem& sod) { for (int i = 0; i < _countof(ShaderObjectIntrinsicData); i++) { if (sod.Kind == ShaderObjectIntrinsicData[i].Kind) { return ShaderObjectIntrinsicData[i]; } } throw std::exception("cannot find shader object kind"); } // The test fixture. class ObjectTest { private: HlslIntellisenseSupport m_isenseSupport; public: BEGIN_TEST_CLASS(ObjectTest) TEST_CLASS_PROPERTY(L"Parallel", L"true") TEST_METHOD_PROPERTY(L"Priority", L"0") END_TEST_CLASS() TEST_CLASS_SETUP(ObjectTestSetup); TEST_METHOD(DeclareLocalObject); TEST_METHOD(OptionalTemplateArgs); TEST_METHOD(MissingTemplateArgs); TEST_METHOD(TooManyTemplateArgs); TEST_METHOD(PassAsParameter); TEST_METHOD(AssignVariables); TEST_METHOD(AssignReturnResult); TEST_METHOD(PassToInoutArgs); TEST_METHOD(TemplateArgConstraints); TEST_METHOD(FunctionInvoke); void FormatTypeNameAndPreamble(const ShaderObjectDataItem& sod, char (&typeName)[64], const char** preambleDecl) { *preambleDecl = ""; auto templateData = GetTemplateData(sod); switch (templateData.TemplateKind) { case SOTK_NoParams: sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); break; case SOTK_SingleSVParam: sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); break; case SOTK_SingleSVCParam: *preambleDecl = "struct MY_STRUCT { float4 f4; bool b; int3 i3; };\n"; sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); break; case SOTK_SVAndSampleCountParams: case SOTK_SVCAndControlPointCountParams: default: sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); break; } } std::string BuildDeclarationFunction(const ShaderObjectDataItem& sod) { return BuildDeclarationFunction(sod, 0, false); } std::string BuildDeclarationFunction(const ShaderObjectDataItem& sod, int missingTemplateCount, bool collapseEmptyArgs) { char result[256]; auto templateData = GetTemplateData(sod); const char StructDecl[] = "struct MY_STRUCT { float4 f4; bool b; int3 i3; };\n"; const char StructParam[] = "\n"; const char VectorParam[] = "\n"; const char VectorAndCountParam[] = "\n"; const char EmptyTemplateArgs[] = "<>"; const char MissingTemplateArgs[] = ""; // Default setup for a 'SOTK_NoParams' case. const char* StructDeclFragment = ""; const char* TemplateDeclFragment = MissingTemplateArgs; if (templateData.TemplateKind == SOTK_SingleSVParam && missingTemplateCount == 0) { TemplateDeclFragment = VectorParam; } else if (templateData.TemplateKind == SOTK_SingleSVCParam && missingTemplateCount == 0) { StructDeclFragment = StructDecl; TemplateDeclFragment = StructParam; } else if ((templateData.TemplateKind == SOTK_SVAndSampleCountParams || templateData.TemplateKind == SOTK_SVCAndControlPointCountParams)) { if (missingTemplateCount == 0) { TemplateDeclFragment = VectorAndCountParam; } else if (missingTemplateCount == 1) { TemplateDeclFragment = VectorParam; } } // Allow 'Object<>' to collapse to 'Object' when collapseEmptyArgs is set. if (templateData.TemplateKind != SOTK_NoParams && TemplateDeclFragment == MissingTemplateArgs) { TemplateDeclFragment = collapseEmptyArgs ? MissingTemplateArgs : EmptyTemplateArgs; } sprintf_s(result, _countof(result), "%s" "float ps(float4 color : COLOR) { %s%s localVar; return 0; }", StructDeclFragment, sod.TypeName, TemplateDeclFragment); return std::string(result); } std::string BuildDeclarationFunctionTooManyArgs(const ShaderObjectDataItem& sod) { char result[256]; auto templateData = GetTemplateData(sod); switch (templateData.TemplateKind) { case SOTK_NoParams: sprintf_s(result, _countof(result), "float ps(float4 color : COLOR) { %s localVar; return 0; }", sod.TypeName); break; case SOTK_SingleSVParam: sprintf_s(result, _countof(result), "float ps(float4 color : COLOR) { %s localVar; return 0; }", sod.TypeName); break; case SOTK_SingleSVCParam: sprintf_s(result, _countof(result), "struct MY_STRUCT { float4 f4; bool b; int3 i3; };\n" "float ps(float4 color : COLOR) { %s localVar; return 0; }", sod.TypeName); break; case SOTK_SVAndSampleCountParams: case SOTK_SVCAndControlPointCountParams: default: sprintf_s(result, _countof(result), "float ps(float4 color : COLOR) { %s localVar; return 0; }", sod.TypeName); break; } return std::string(result); } std::string BuildPassAsParameter(const ShaderObjectDataItem& sod) { char result[256]; char typeName[64]; const char* preambleDecl; FormatTypeNameAndPreamble(sod, typeName, &preambleDecl); sprintf_s(result, _countof(result), "%s" "void f(%s parameter) { }\n" "float ps(float4 color : COLOR) { %s localVar; f(localVar); return 0; }", preambleDecl, typeName, typeName); return std::string(result); } std::string BuildAssignment(const ShaderObjectDataItem& sod) { char result[256]; char typeName[64]; const char* preambleDecl; FormatTypeNameAndPreamble(sod, typeName, &preambleDecl); sprintf_s(result, _countof(result), "%s" "float ps(float4 color : COLOR) { %s lv1; %s lv2; lv1 = lv2; return 0; }", preambleDecl, typeName, typeName); return std::string(result); } std::string BuildAssignmentFromResult(const ShaderObjectDataItem& sod) { char result[256]; char typeName[64]; const char* preambleDecl; FormatTypeNameAndPreamble(sod, typeName, &preambleDecl); sprintf_s(result, _countof(result), "%s" "%s f() { %s local; return local; }\n" "float ps(float4 color : COLOR) { %s lv1 = f(); return 0; }", preambleDecl, typeName, typeName, typeName); return std::string(result); } void CheckCompiles(const std::string& text, bool expected) { CheckCompiles(text.c_str(), text.size(), expected); } void CheckCompiles(const char* text, size_t textLen, bool expected) { CompilationResult result( CompilationResult::CreateForProgram(text, textLen)); // Uncomment the line to print out the AST unconditionally. // printf("%s", result.BuildASTString().c_str()); if (expected != result.ParseSucceeded()) { EXPECT_EQ(expected, result.ParseSucceeded()); // TODO: log this out //<< "for program " << text << "\n with AST\n" << result.BuildASTString() //<< "and errors\n" << result.GetTextForErrors(); } } }; bool ObjectTest::ObjectTestSetup() { // Runs once for all tests in this class. // Load the dxcompiler DLL and hold a reference to it to avoid unload/reloads. m_isenseSupport.Initialize(); return m_isenseSupport.IsEnabled(); } TEST_F(ObjectTest, DeclareLocalObject) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); CheckCompiles(BuildDeclarationFunction(sod), true); } } TEST_F(ObjectTest, OptionalTemplateArgs) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); const ShaderObjectTemplateDataItem& templateData = GetTemplateData(sod); int argCount = CountOptionalTemplateArguments(templateData); if (argCount == 0) { continue; } for (int missingCount = 1; missingCount <= argCount; missingCount++) { CheckCompiles(BuildDeclarationFunction(sod, missingCount, false), true); } } } TEST_F(ObjectTest, MissingTemplateArgs) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); const ShaderObjectTemplateDataItem& templateData = GetTemplateData(sod); int argCount = CountOptionalTemplateArguments(templateData); if (argCount == 0) { continue; } CheckCompiles(BuildDeclarationFunction(sod, argCount, true), true); } } TEST_F(ObjectTest, TooManyTemplateArgs) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); CheckCompiles(BuildDeclarationFunctionTooManyArgs(sod), false); } } TEST_F(ObjectTest, PassAsParameter) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); CheckCompiles(BuildPassAsParameter(sod), true); } } TEST_F(ObjectTest, AssignVariables) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); CheckCompiles(BuildAssignment(sod), true); } } TEST_F(ObjectTest, AssignReturnResult) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); CheckCompiles(BuildAssignmentFromResult(sod), true); } } TEST_F(ObjectTest, PassToInoutArgs) { for (const auto &sod : ShaderObjectData) { // Speed up the test by building one large program with all inout parameter modifiers per object. std::stringstream programText; unsigned uniqueId = 0; for (const auto &iop : InOutParameterModifierData) { char typeName[64]; const char* preambleDecl; // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); FormatTypeNameAndPreamble(sod, typeName, &preambleDecl); if (uniqueId == 0) { // do this only once programText << preambleDecl << std::endl; } programText << "float ps_" << uniqueId << "(" << iop.Keyword << " " << typeName << " o) { return 1.0f; }" << std::endl; programText << "void caller_" << uniqueId << "() { " << typeName << " lv; ps_" << uniqueId << "(lv); }" << std::endl; programText << std::endl; uniqueId++; } std::string programTextStr(programText.str()); CheckCompiles(programTextStr.c_str(), programTextStr.size(), true); } } class TemplateSampleDataItem { public: TemplateSampleDataItem() { } TemplateSampleDataItem(const TemplateSampleDataItem& other) : Preamble(other.Preamble), TypeName(other.TypeName), IsValid(other.IsValid) { } TemplateSampleDataItem(const char* preamble, const char* typeName, bool isValid) : Preamble(preamble), TypeName(typeName), IsValid(isValid) { } std::string Preamble; std::string TypeName; bool IsValid; }; std::vector CreateSampleDataForTemplateArg( const ShaderObjectDataItem& sod, int templateIndex) { std::vector result; char typeName[64]; auto templateData = GetTemplateData(sod); assert(templateData.TemplateKind != SOTK_NoParams && "shouldn't call CreateSampleDataForTemplateArg"); switch (templateData.TemplateKind) { case SOTK_SingleSVParam: case SOTK_SingleSVCParam: sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, true)); sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, false)); break; case SOTK_SVAndSampleCountParams: case SOTK_SVCAndControlPointCountParams: if (templateIndex == 0) { sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, true)); sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, false)); } else { sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, true)); sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, true)); // These are deferred to back-end validation for now. // bool largeNumberValid = sod.Kind == SOK_InputPatch || sod.Kind == SOK_OutputPatch; sprintf_s(typeName, _countof(typeName), "%s", sod.TypeName); result.push_back(TemplateSampleDataItem("", typeName, false)); } break; } return result; } TEST_F(ObjectTest, TemplateArgConstraints) { for (const auto &sod : ShaderObjectData) { // When shader models are validated, run through all of them. // for (const auto &st : sod.ValidShaderTypes) const auto st = std::first(sod.ValidShaderTypes); const ShaderObjectTemplateDataItem& templateData = GetTemplateData(sod); int argCount = CountOptionalTemplateArguments(templateData); if (argCount == 0) { continue; } for (int i = 0; i < argCount; i++) { std::vector sampleData = CreateSampleDataForTemplateArg(sod, i); for (auto sampleDataItem : sampleData) { char result[256]; sprintf_s(result, _countof(result), "%s" "float ps(float4 color : COLOR) { %s lv1; %s lv2; lv1 = lv2; return 0; }", sampleDataItem.Preamble.c_str(), sampleDataItem.TypeName.c_str(), sampleDataItem.TypeName.c_str()); CheckCompiles(result, strlen(result), sampleDataItem.IsValid); } } } } static std::string SelectComponentType(BYTE legalTypes) { switch ((LEGAL_INTRINSIC_COMPTYPES)legalTypes) { case LICOMPTYPE_VOID: return "void"; case LICOMPTYPE_BOOL: return "bool"; case LICOMPTYPE_INT: return "int"; case LICOMPTYPE_UINT: return "uint"; case LICOMPTYPE_ANY_INT: return "int"; case LICOMPTYPE_ANY_INT32: return "int"; case LICOMPTYPE_UINT_ONLY: return "uint"; case LICOMPTYPE_FLOAT: return "float"; case LICOMPTYPE_ANY_FLOAT: return "float"; case LICOMPTYPE_FLOAT_LIKE: return "float"; case LICOMPTYPE_FLOAT_DOUBLE: return "double"; case LICOMPTYPE_DOUBLE: return "double"; case LICOMPTYPE_DOUBLE_ONLY: return "double"; case LICOMPTYPE_NUMERIC: return "int"; case LICOMPTYPE_NUMERIC32: return "float"; case LICOMPTYPE_NUMERIC32_ONLY: return "double"; case LICOMPTYPE_ANY: return "int"; default: return ""; //LICOMPTYPE_SAMPLER1D, //LICOMPTYPE_SAMPLER2D, //LICOMPTYPE_SAMPLER3D, //LICOMPTYPE_SAMPLERCUBE, //LICOMPTYPE_SAMPLERCMP, //LICOMPTYPE_SAMPLER, //LICOMPTYPE_STRING, } } TEST_F(ObjectTest, FunctionInvoke) { // This is pending complete support for primitive types - there are many // instances of uint usage, which isn't currently supported. #if 0 // Tests for too many or too few arguments are available as lit-based tests. // Invoke each method, assigning the result of each method invocation as necessary. uint64_t iteration = 0; for (const auto &sod : ShaderObjectData) { const ShaderObjectIntrinsicDataItem& intrinsicData = GetIntrinsicData(sod); const ShaderObjectTemplateDataItem& templateData = GetTemplateData(sod); for (size_t i = 0; i < intrinsicData.IntrinsicCount; i++) { const HLSL_INTRINSIC* intrinsic = &intrinsicData.Intrinsics[i]; ++iteration; // Build a program that will call this intrinsic. // Select an element type for the class if needed. std::string objectType; std::string elementType; std::string elementParameters; switch (templateData.TemplateKind) { case ShaderObjectTemplateKind::SOTK_NoParams: break; case ShaderObjectTemplateKind::SOTK_SingleSVCParam: case ShaderObjectTemplateKind::SOTK_SingleSVParam: elementParameters = ""; elementType = "float3"; break; case ShaderObjectTemplateKind::SOTK_SVAndSampleCountParams: case ShaderObjectTemplateKind::SOTK_SVCAndControlPointCountParams: elementParameters = ""; elementType = "float3"; break; } objectType = sod.TypeName + elementParameters; // Select types for all arguments. // This implementation is brain-dead but (a) different from production, // so it's a sensible oracle, and (b) very straightforward. We simply // keep instantiating argument types until we finished. std::string argumentTypes[g_MaxIntrinsicParamCount]; { bool madeProgress = false; int argsRemaining = intrinsic->uNumArgs - 1; do { madeProgress = false; for (int i = 1; i < intrinsic->uNumArgs; i++) { if (!argumentTypes[i - 1].empty()) { continue; } // Determine whether we can make an independent selection. if (intrinsic->pArgs[i].uTemplateId == INTRIN_TEMPLATE_FROM_TYPE) { argumentTypes[i - 1] = elementType; madeProgress = true; --argsRemaining; continue; } // An independent scalar. if (intrinsic->pArgs[i].uTemplateId == i && intrinsic->pArgs[i].uComponentTypeId == i && intrinsic->pArgs[i].uLegalComponentTypes != LITEMPLATE_ANY && intrinsic->pArgs[i].uLegalTemplates == LITEMPLATE_SCALAR) { argumentTypes[i - 1] = SelectComponentType(intrinsic->pArgs[i].uLegalComponentTypes); madeProgress = true; --argsRemaining; continue; } // An independent vector. if (intrinsic->pArgs[i].uTemplateId == i && intrinsic->pArgs[i].uComponentTypeId == i && intrinsic->pArgs[i].uLegalComponentTypes != LITEMPLATE_ANY && intrinsic->pArgs[i].uLegalTemplates == LITEMPLATE_VECTOR) { argumentTypes[i - 1] = SelectComponentType(intrinsic->pArgs[i].uLegalComponentTypes) + "2"; madeProgress = true; --argsRemaining; continue; } // TODO: complete the assignment cases with primitive types // Determine whether we have all dependencies assigned for a selection. } } while (madeProgress && argsRemaining > 0); EXPECT_EQ(0, argsRemaining) << "otherwise we're unable to complete the signature for " << intrinsic->pArgs[0].pName << " in iteration " << iteration; } // Determine what the result type is. bool resultIsVoid = intrinsic->pArgs[0].uLegalTemplates == LITEMPLATE_VOID; std::string resultType; if (!resultIsVoid) { } // Assemble the program as a series of declarations, a method call // and possibly an assignment if there's a return value. std::stringstream program; std::string assignmentLeftHand(resultIsVoid ? "" : (resultType + " result = ")); program << "// iteration " << iteration << "\n" "void f() {\n" " " << objectType << " testObject;\n"; for (int i = 1; i < intrinsic->uNumArgs; i++) { program << argumentTypes[i - 1] << ' ' << intrinsic->pArgs[i].pName << ";\n"; } program << assignmentLeftHand << "testObject." << intrinsic->pArgs[0].pName << "("; for (int i = 1; i < intrinsic->uNumArgs; i++) { program << intrinsic->pArgs[i].pName; if (i != intrinsic->uNumArgs - 1) { program << ", "; } } program << ");\n}"; // Verify it runs. CheckCompiles(program.str(), true); } } #endif } // TODO: force type promotion to occur for arguments.