Author: Finn Plummer Date: 2025-05-29T10:18:07-07:00 New Revision: a926c61cc005a5a37bcb62f0dbd2c18c0baf91d3
URL: https://github.com/llvm/llvm-project/commit/a926c61cc005a5a37bcb62f0dbd2c18c0baf91d3 DIFF: https://github.com/llvm/llvm-project/commit/a926c61cc005a5a37bcb62f0dbd2c18c0baf91d3.diff LOG: [HLSL][RootSignature] Add parsing of floats for StaticSampler (#140181) - defines in-memory representaiton of MipLODBias to allow for testing of a float parameter - defines `handleInt` and `handleFloat` to handle converting a token's `NumSpelling` into a valid float - plugs this into `parseFloatParam` to fill in the MipLODBias param The parsing of floats is required to match the behaviour of DXC. This behaviour is outlined as follows: - if the number is an integer then convert it using `_atoi64`, check for overflow and static_cast this to a float - if the number is a float then convert it using `strtod`, check for float overflow and static_cast this to a float, this will implicitly also check for double over/underflow and if the string is malformed then it will return an error This pr matches this behaviour by parsing as, uint/int accordingly and then casting, or, by using the correct APFloat semantics/rounding mode with `NumericLiteralParser`. - adds testing of error diagnostics and valid float param values to demonstrate functionality Part 2 of https://github.com/llvm/llvm-project/issues/126574 Added: Modified: clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Lex/HLSLRootSignatureTokenKinds.def clang/include/clang/Parse/ParseHLSLRootSignature.h clang/lib/Parse/ParseHLSLRootSignature.cpp clang/unittests/Lex/LexHLSLRootSignatureTest.cpp clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index fd525140d0482..554d70de86902 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1856,7 +1856,11 @@ def err_hlsl_unexpected_end_of_params : Error<"expected %0 to denote end of parameters, or, another valid parameter of %1">; def err_hlsl_rootsig_repeat_param : Error<"specified the same parameter '%0' multiple times">; def err_hlsl_rootsig_missing_param : Error<"did not specify mandatory parameter '%0'">; -def err_hlsl_number_literal_overflow : Error<"integer literal is too large to be represented as a 32-bit %select{signed |}0 integer type">; +def err_hlsl_number_literal_overflow : Error< + "%select{integer|float}0 literal is too large to be represented as a " + "%select{32-bit %select{signed|}1 integer|float}0 type">; +def err_hlsl_number_literal_underflow : Error< + "float literal has a magnitude that is too small to be represented as a float type">; def err_hlsl_rootsig_non_zero_flag : Error<"flag value is neither a literal 0 nor a named value">; } // end of Parser diagnostics diff --git a/clang/include/clang/Lex/HLSLRootSignatureTokenKinds.def b/clang/include/clang/Lex/HLSLRootSignatureTokenKinds.def index ddebe82987197..5d16eaa5b72f6 100644 --- a/clang/include/clang/Lex/HLSLRootSignatureTokenKinds.def +++ b/clang/include/clang/Lex/HLSLRootSignatureTokenKinds.def @@ -100,6 +100,9 @@ KEYWORD(flags) KEYWORD(numDescriptors) KEYWORD(offset) +// StaticSampler Keywords: +KEYWORD(mipLODBias) + // Unbounded Enum: UNBOUNDED_ENUM(unbounded, "unbounded") diff --git a/clang/include/clang/Parse/ParseHLSLRootSignature.h b/clang/include/clang/Parse/ParseHLSLRootSignature.h index da17388a2aea5..a03f33dfd3b4e 100644 --- a/clang/include/clang/Parse/ParseHLSLRootSignature.h +++ b/clang/include/clang/Parse/ParseHLSLRootSignature.h @@ -111,12 +111,14 @@ class RootSignatureParser { struct ParsedStaticSamplerParams { std::optional<llvm::hlsl::rootsig::Register> Reg; + std::optional<float> MipLODBias; }; std::optional<ParsedStaticSamplerParams> parseStaticSamplerParams(); // Common parsing methods std::optional<uint32_t> parseUIntParam(); std::optional<llvm::hlsl::rootsig::Register> parseRegister(); + std::optional<float> parseFloatParam(); /// Parsing methods of various enums std::optional<llvm::hlsl::rootsig::ShaderVisibility> parseShaderVisibility(); @@ -128,6 +130,19 @@ class RootSignatureParser { /// Use NumericLiteralParser to convert CurToken.NumSpelling into a unsigned /// 32-bit integer std::optional<uint32_t> handleUIntLiteral(); + /// Use NumericLiteralParser to convert CurToken.NumSpelling into a signed + /// 32-bit integer + std::optional<int32_t> handleIntLiteral(bool Negated); + /// Use NumericLiteralParser to convert CurToken.NumSpelling into a float + /// + /// This matches the behaviour of DXC, which is as follows: + /// - convert the spelling with `strtod` + /// - check for a float overflow + /// - cast the double to a float + /// The behaviour of `strtod` is replicated using: + /// Semantics: llvm::APFloat::Semantics::S_IEEEdouble + /// RoundingMode: llvm::RoundingMode::NearestTiesToEven + std::optional<float> handleFloatLiteral(bool Negated); /// Flags may specify the value of '0' to denote that there should be no /// flags set. diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp index a37505e360fc0..8be91f5991b21 100644 --- a/clang/lib/Parse/ParseHLSLRootSignature.cpp +++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp @@ -376,6 +376,10 @@ std::optional<StaticSampler> RootSignatureParser::parseStaticSampler() { Sampler.Reg = Params->Reg.value(); + // Fill in optional values + if (Params->MipLODBias.has_value()) + Sampler.MipLODBias = Params->MipLODBias.value(); + if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_hlsl_unexpected_end_of_params, /*param of=*/TokenKind::kw_StaticSampler)) @@ -661,6 +665,23 @@ RootSignatureParser::parseStaticSamplerParams() { return std::nullopt; Params.Reg = Reg; } + + // `mipLODBias` `=` NUMBER + if (tryConsumeExpectedToken(TokenKind::kw_mipLODBias)) { + if (Params.MipLODBias.has_value()) { + getDiags().Report(CurToken.TokLoc, diag::err_hlsl_rootsig_repeat_param) + << CurToken.TokKind; + return std::nullopt; + } + + if (consumeExpectedToken(TokenKind::pu_equal)) + return std::nullopt; + + auto MipLODBias = parseFloatParam(); + if (!MipLODBias.has_value()) + return std::nullopt; + Params.MipLODBias = MipLODBias; + } } while (tryConsumeExpectedToken(TokenKind::pu_comma)); return Params; @@ -709,6 +730,39 @@ std::optional<Register> RootSignatureParser::parseRegister() { return Reg; } +std::optional<float> RootSignatureParser::parseFloatParam() { + assert(CurToken.TokKind == TokenKind::pu_equal && + "Expects to only be invoked starting at given keyword"); + // Consume sign modifier + bool Signed = + tryConsumeExpectedToken({TokenKind::pu_plus, TokenKind::pu_minus}); + bool Negated = Signed && CurToken.TokKind == TokenKind::pu_minus; + + // DXC will treat a postive signed integer as unsigned + if (!Negated && tryConsumeExpectedToken(TokenKind::int_literal)) { + std::optional<uint32_t> UInt = handleUIntLiteral(); + if (!UInt.has_value()) + return std::nullopt; + return float(UInt.value()); + } + + if (Negated && tryConsumeExpectedToken(TokenKind::int_literal)) { + std::optional<int32_t> Int = handleIntLiteral(Negated); + if (!Int.has_value()) + return std::nullopt; + return float(Int.value()); + } + + if (tryConsumeExpectedToken(TokenKind::float_literal)) { + std::optional<float> Float = handleFloatLiteral(Negated); + if (!Float.has_value()) + return std::nullopt; + return Float.value(); + } + + return std::nullopt; +} + std::optional<llvm::hlsl::rootsig::ShaderVisibility> RootSignatureParser::parseShaderVisibility() { assert(CurToken.TokKind == TokenKind::pu_equal && @@ -819,22 +873,121 @@ std::optional<uint32_t> RootSignatureParser::handleUIntLiteral() { PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo(), PP.getDiagnostics()); if (Literal.hadError) - return true; // Error has already been reported so just return + return std::nullopt; // Error has already been reported so just return - assert(Literal.isIntegerLiteral() && "IsNumberChar will only support digits"); + assert(Literal.isIntegerLiteral() && + "NumSpelling can only consist of digits"); - llvm::APSInt Val = llvm::APSInt(32, false); + llvm::APSInt Val(32, /*IsUnsigned=*/true); if (Literal.GetIntegerValue(Val)) { // Report that the value has overflowed PP.getDiagnostics().Report(CurToken.TokLoc, diag::err_hlsl_number_literal_overflow) - << 0 << CurToken.NumSpelling; + << /*integer type*/ 0 << /*is signed*/ 0; return std::nullopt; } return Val.getExtValue(); } +std::optional<int32_t> RootSignatureParser::handleIntLiteral(bool Negated) { + // Parse the numeric value and do semantic checks on its specification + clang::NumericLiteralParser Literal(CurToken.NumSpelling, CurToken.TokLoc, + PP.getSourceManager(), PP.getLangOpts(), + PP.getTargetInfo(), PP.getDiagnostics()); + if (Literal.hadError) + return std::nullopt; // Error has already been reported so just return + + assert(Literal.isIntegerLiteral() && + "NumSpelling can only consist of digits"); + + llvm::APSInt Val(32, /*IsUnsigned=*/true); + // GetIntegerValue will overwrite Val from the parsed Literal and return + // true if it overflows as a 32-bit unsigned int + bool Overflowed = Literal.GetIntegerValue(Val); + + // So we then need to check that it doesn't overflow as a 32-bit signed int: + int64_t MaxNegativeMagnitude = -int64_t(std::numeric_limits<int32_t>::min()); + Overflowed |= (Negated && MaxNegativeMagnitude < Val.getExtValue()); + + int64_t MaxPositiveMagnitude = int64_t(std::numeric_limits<int32_t>::max()); + Overflowed |= (!Negated && MaxPositiveMagnitude < Val.getExtValue()); + + if (Overflowed) { + // Report that the value has overflowed + PP.getDiagnostics().Report(CurToken.TokLoc, + diag::err_hlsl_number_literal_overflow) + << /*integer type*/ 0 << /*is signed*/ 1; + return std::nullopt; + } + + if (Negated) + Val = -Val; + + return int32_t(Val.getExtValue()); +} + +std::optional<float> RootSignatureParser::handleFloatLiteral(bool Negated) { + // Parse the numeric value and do semantic checks on its specification + clang::NumericLiteralParser Literal(CurToken.NumSpelling, CurToken.TokLoc, + PP.getSourceManager(), PP.getLangOpts(), + PP.getTargetInfo(), PP.getDiagnostics()); + if (Literal.hadError) + return std::nullopt; // Error has already been reported so just return + + assert(Literal.isFloatingLiteral() && + "NumSpelling consists only of [0-9.ef+-]. Any malformed NumSpelling " + "will be caught and reported by NumericLiteralParser."); + + // DXC used `strtod` to convert the token string to a float which corresponds + // to: + auto DXCSemantics = llvm::APFloat::Semantics::S_IEEEdouble; + auto DXCRoundingMode = llvm::RoundingMode::NearestTiesToEven; + + llvm::APFloat Val(llvm::APFloat::EnumToSemantics(DXCSemantics)); + llvm::APFloat::opStatus Status(Literal.GetFloatValue(Val, DXCRoundingMode)); + + // Note: we do not error when opStatus::opInexact by itself as this just + // denotes that rounding occured but not that it is invalid + assert(!(Status & llvm::APFloat::opStatus::opInvalidOp) && + "NumSpelling consists only of [0-9.ef+-]. Any malformed NumSpelling " + "will be caught and reported by NumericLiteralParser."); + + assert(!(Status & llvm::APFloat::opStatus::opDivByZero) && + "It is not possible for a division to be performed when " + "constructing an APFloat from a string"); + + if (Status & llvm::APFloat::opStatus::opUnderflow) { + // Report that the value has underflowed + PP.getDiagnostics().Report(CurToken.TokLoc, + diag::err_hlsl_number_literal_underflow); + return std::nullopt; + } + + if (Status & llvm::APFloat::opStatus::opOverflow) { + // Report that the value has overflowed + PP.getDiagnostics().Report(CurToken.TokLoc, + diag::err_hlsl_number_literal_overflow) + << /*float type*/ 1; + return std::nullopt; + } + + if (Negated) + Val = -Val; + + double DoubleVal = Val.convertToDouble(); + double FloatMax = double(std::numeric_limits<float>::max()); + if (FloatMax < DoubleVal || DoubleVal < -FloatMax) { + // Report that the value has overflowed + PP.getDiagnostics().Report(CurToken.TokLoc, + diag::err_hlsl_number_literal_overflow) + << /*float type*/ 1; + return std::nullopt; + } + + return static_cast<float>(DoubleVal); +} + bool RootSignatureParser::verifyZeroFlag() { assert(CurToken.TokKind == TokenKind::int_literal); auto X = handleUIntLiteral(); diff --git a/clang/unittests/Lex/LexHLSLRootSignatureTest.cpp b/clang/unittests/Lex/LexHLSLRootSignatureTest.cpp index 3e38c281f4fb1..b610b8f10f8da 100644 --- a/clang/unittests/Lex/LexHLSLRootSignatureTest.cpp +++ b/clang/unittests/Lex/LexHLSLRootSignatureTest.cpp @@ -136,6 +136,8 @@ TEST_F(LexHLSLRootSignatureTest, ValidLexAllTokensTest) { space visibility flags numDescriptors offset + mipLODBias + unbounded DESCRIPTOR_RANGE_OFFSET_APPEND diff --git a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp index cd3b12dbe4d24..b08c07f41141e 100644 --- a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp +++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp @@ -225,7 +225,7 @@ TEST_F(ParseHLSLRootSignatureTest, ValidParseDTClausesTest) { TEST_F(ParseHLSLRootSignatureTest, ValidParseStaticSamplerTest) { const llvm::StringLiteral Source = R"cc( - StaticSampler(s0) + StaticSampler(s0, mipLODBias = 0) )cc"; TrivialModuleLoader ModLoader; @@ -247,6 +247,92 @@ TEST_F(ParseHLSLRootSignatureTest, ValidParseStaticSamplerTest) { ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); ASSERT_EQ(std::get<StaticSampler>(Elem).Reg.ViewType, RegisterType::SReg); ASSERT_EQ(std::get<StaticSampler>(Elem).Reg.Number, 0u); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 0.f); + + ASSERT_TRUE(Consumer->isSatisfied()); +} + +TEST_F(ParseHLSLRootSignatureTest, ValidParseFloatsTest) { + const llvm::StringLiteral Source = R"cc( + StaticSampler(s0, mipLODBias = 0), + StaticSampler(s0, mipLODBias = +1), + StaticSampler(s0, mipLODBias = -1), + StaticSampler(s0, mipLODBias = 42.), + StaticSampler(s0, mipLODBias = +4.2), + StaticSampler(s0, mipLODBias = -.42), + StaticSampler(s0, mipLODBias = .42e+3), + StaticSampler(s0, mipLODBias = 42E-12), + StaticSampler(s0, mipLODBias = 42.f), + StaticSampler(s0, mipLODBias = 4.2F), + StaticSampler(s0, mipLODBias = 42.e+10f), + StaticSampler(s0, mipLODBias = -2147483648), + StaticSampler(s0, mipLODBias = 2147483648), + )cc"; + + TrivialModuleLoader ModLoader; + auto PP = createPP(Source, ModLoader); + auto TokLoc = SourceLocation(); + + hlsl::RootSignatureLexer Lexer(Source, TokLoc); + SmallVector<RootElement> Elements; + hlsl::RootSignatureParser Parser(Elements, Lexer, *PP); + + // Test no diagnostics produced + Consumer->setNoDiag(); + + ASSERT_FALSE(Parser.parse()); + + RootElement Elem = Elements[0]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 0.f); + + Elem = Elements[1]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 1.f); + + Elem = Elements[2]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, -1.f); + + Elem = Elements[3]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 42.f); + + Elem = Elements[4]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 4.2f); + + Elem = Elements[5]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, -.42f); + + Elem = Elements[6]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 420.f); + + Elem = Elements[7]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 0.000000000042f); + + Elem = Elements[8]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 42.f); + + Elem = Elements[9]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 4.2f); + + Elem = Elements[10]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 420000000000.f); + + Elem = Elements[11]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, -2147483648.f); + + Elem = Elements[12]; + ASSERT_TRUE(std::holds_alternative<StaticSampler>(Elem)); + ASSERT_FLOAT_EQ(std::get<StaticSampler>(Elem).MipLODBias, 2147483648.f); ASSERT_TRUE(Consumer->isSatisfied()); } @@ -717,6 +803,112 @@ TEST_F(ParseHLSLRootSignatureTest, InvalidLexOverflowedNumberTest) { ASSERT_TRUE(Consumer->isSatisfied()); } +TEST_F(ParseHLSLRootSignatureTest, InvalidParseOverflowedNegativeNumberTest) { + // This test will check that parsing fails due to a unsigned integer having + // too large of a magnitude to be interpreted as its negative + const llvm::StringLiteral Source = R"cc( + StaticSampler(s0, mipLODBias = -4294967295) + )cc"; + + TrivialModuleLoader ModLoader; + auto PP = createPP(Source, ModLoader); + auto TokLoc = SourceLocation(); + + hlsl::RootSignatureLexer Lexer(Source, TokLoc); + SmallVector<RootElement> Elements; + hlsl::RootSignatureParser Parser(Elements, Lexer, *PP); + + // Test correct diagnostic produced + Consumer->setExpected(diag::err_hlsl_number_literal_overflow); + ASSERT_TRUE(Parser.parse()); + + ASSERT_TRUE(Consumer->isSatisfied()); +} + +TEST_F(ParseHLSLRootSignatureTest, InvalidLexOverflowedFloatTest) { + // This test will check that the lexing fails due to a float overflow + const llvm::StringLiteral Source = R"cc( + StaticSampler(s0, mipLODBias = 3.402823467e+38F) + )cc"; + + TrivialModuleLoader ModLoader; + auto PP = createPP(Source, ModLoader); + auto TokLoc = SourceLocation(); + + hlsl::RootSignatureLexer Lexer(Source, TokLoc); + SmallVector<RootElement> Elements; + hlsl::RootSignatureParser Parser(Elements, Lexer, *PP); + + // Test correct diagnostic produced + Consumer->setExpected(diag::err_hlsl_number_literal_overflow); + ASSERT_TRUE(Parser.parse()); + + ASSERT_TRUE(Consumer->isSatisfied()); +} + +TEST_F(ParseHLSLRootSignatureTest, InvalidLexNegOverflowedFloatTest) { + // This test will check that the lexing fails due to negative float overflow + const llvm::StringLiteral Source = R"cc( + StaticSampler(s0, mipLODBias = -3.402823467e+38F) + )cc"; + + TrivialModuleLoader ModLoader; + auto PP = createPP(Source, ModLoader); + auto TokLoc = SourceLocation(); + + hlsl::RootSignatureLexer Lexer(Source, TokLoc); + SmallVector<RootElement> Elements; + hlsl::RootSignatureParser Parser(Elements, Lexer, *PP); + + // Test correct diagnostic produced + Consumer->setExpected(diag::err_hlsl_number_literal_overflow); + ASSERT_TRUE(Parser.parse()); + + ASSERT_TRUE(Consumer->isSatisfied()); +} + +TEST_F(ParseHLSLRootSignatureTest, InvalidLexOverflowedDoubleTest) { + // This test will check that the lexing fails due to an overflow of double + const llvm::StringLiteral Source = R"cc( + StaticSampler(s0, mipLODBias = 1.e+500) + )cc"; + + TrivialModuleLoader ModLoader; + auto PP = createPP(Source, ModLoader); + auto TokLoc = SourceLocation(); + + hlsl::RootSignatureLexer Lexer(Source, TokLoc); + SmallVector<RootElement> Elements; + hlsl::RootSignatureParser Parser(Elements, Lexer, *PP); + + // Test correct diagnostic produced + Consumer->setExpected(diag::err_hlsl_number_literal_overflow); + ASSERT_TRUE(Parser.parse()); + + ASSERT_TRUE(Consumer->isSatisfied()); +} + +TEST_F(ParseHLSLRootSignatureTest, InvalidLexUnderflowFloatTest) { + // This test will check that the lexing fails due to double underflow + const llvm::StringLiteral Source = R"cc( + StaticSampler(s0, mipLODBias = 10e-309) + )cc"; + + TrivialModuleLoader ModLoader; + auto PP = createPP(Source, ModLoader); + auto TokLoc = SourceLocation(); + + hlsl::RootSignatureLexer Lexer(Source, TokLoc); + SmallVector<RootElement> Elements; + hlsl::RootSignatureParser Parser(Elements, Lexer, *PP); + + // Test correct diagnostic produced + Consumer->setExpected(diag::err_hlsl_number_literal_underflow); + ASSERT_TRUE(Parser.parse()); + + ASSERT_TRUE(Consumer->isSatisfied()); +} + TEST_F(ParseHLSLRootSignatureTest, InvalidNonZeroFlagsTest) { // This test will check that parsing fails when a non-zero integer literal // is given to flags diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h index f9de86b567fea..fea9a9962991c 100644 --- a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h +++ b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h @@ -157,6 +157,7 @@ raw_ostream &operator<<(raw_ostream &OS, const DescriptorTableClause &Clause); struct StaticSampler { Register Reg; + float MipLODBias = 0.f; }; /// Models RootElement : RootFlags | RootConstants | RootParam _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits