Skip to content

Commit 4ae4313

Browse files
HLSL2GLSL Converter: handle preprocessor-defined shader function return type
1 parent 431f4ee commit 4ae4313

4 files changed

Lines changed: 238 additions & 55 deletions

File tree

Graphics/HLSL2GLSLConverterLib/include/HLSL2GLSLConverterImpl.hpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2022 Diligent Graphics LLC
2+
* Copyright 2019-2024 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -257,6 +257,7 @@ class HLSL2GLSLConverterImpl
257257
TokenType Type = TokenType::Undefined;
258258
String Literal;
259259
String Delimiter;
260+
size_t Idx = ~size_t{0};
260261

261262
void SetType(TokenType _Type)
262263
{
@@ -303,19 +304,22 @@ class HLSL2GLSLConverterImpl
303304
const std::string::const_iterator& DelimStart,
304305
const std::string::const_iterator& DelimEnd,
305306
const std::string::const_iterator& LiteralStart,
306-
const std::string::const_iterator& LiteralEnd)
307+
const std::string::const_iterator& LiteralEnd,
308+
size_t Idx)
307309
{
308-
return TokenInfo{_Type, std::string{LiteralStart, LiteralEnd}, std::string{DelimStart, DelimEnd}};
310+
return TokenInfo{_Type, std::string{LiteralStart, LiteralEnd}, std::string{DelimStart, DelimEnd}, Idx};
309311
}
310312

311313
TokenInfo() {}
312314

313315
TokenInfo(TokenType _Type,
314316
std::string _Literal,
315-
std::string _Delimiter = "") :
317+
std::string _Delimiter = "",
318+
size_t _Idx = ~size_t{0}) :
316319
Type{_Type},
317320
Literal{std::move(_Literal)},
318-
Delimiter{std::move(_Delimiter)}
321+
Delimiter{std::move(_Delimiter)},
322+
Idx{_Idx}
319323
{}
320324

321325
size_t GetDelimiterLen() const
@@ -402,6 +406,8 @@ class HLSL2GLSLConverterImpl
402406

403407
const HLSLObjectInfo* FindHLSLObject(const String& Name);
404408

409+
void ParseGlobalPreprocessorDefines();
410+
405411
void ProcessShaderDeclaration(TokenListType::iterator EntryPointToken, SHADER_TYPE ShaderType);
406412

407413
void ProcessObjectMethods(const TokenListType::iterator& ScopeStart, const TokenListType::iterator& ScopeEnd);
@@ -586,6 +592,9 @@ class HLSL2GLSLConverterImpl
586592
// List of tokens defining structs
587593
std::unordered_map<HashMapStringKey, TokenListType::iterator> m_StructDefinitions;
588594

595+
// List of preprocessor macro definitions in global scope
596+
std::unordered_map<HashMapStringKey, TokenListType::iterator> m_PreprocessorDefinitions;
597+
589598
// Stack of parsed objects, for every scope level.
590599
// There are currently only two levels:
591600
// level 0 - global scope, contains all global objects

Graphics/HLSL2GLSLConverterLib/src/HLSL2GLSLConverterImpl.cpp

Lines changed: 170 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2022 Diligent Graphics LLC
2+
* Copyright 2019-2024 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -813,8 +813,18 @@ void HLSL2GLSLConverterImpl::ConversionStream::InsertIncludes(String& GLSLSource
813813
// The function converts source code into a token list
814814
void HLSL2GLSLConverterImpl::ConversionStream::Tokenize(const String& Source)
815815
{
816+
size_t TokenIdx = 0;
817+
816818
m_Tokens = Parsing::Tokenize<TokenInfo, decltype(m_Tokens)>(
817-
Source.begin(), Source.end(), TokenInfo::Create,
819+
Source.begin(), Source.end(),
820+
[&TokenIdx](TokenType Type,
821+
const std::string::const_iterator& DelimStart,
822+
const std::string::const_iterator& DelimEnd,
823+
const std::string::const_iterator& LiteralStart,
824+
const std::string::const_iterator& LiteralEnd) //
825+
{
826+
return TokenInfo::Create(Type, DelimStart, DelimEnd, LiteralStart, LiteralEnd, TokenIdx++);
827+
},
818828
[&](const std::string::const_iterator& Start, const std::string::const_iterator& End) //
819829
{
820830
auto KeywordIt = m_Converter.m_HLSLKeywords.find(HashMapStringKey{std::string{Start, End}});
@@ -827,6 +837,71 @@ void HLSL2GLSLConverterImpl::ConversionStream::Tokenize(const String& Source)
827837
});
828838
}
829839

840+
void HLSL2GLSLConverterImpl::ConversionStream::ParseGlobalPreprocessorDefines()
841+
{
842+
auto Token = m_Tokens.begin();
843+
844+
// Collect global-scope preprocessor definitions
845+
int PreprocessorScopeLevel = 0;
846+
while (Token != m_Tokens.end())
847+
{
848+
if (Token->Type != TokenType::PreprocessorDirective)
849+
{
850+
++Token;
851+
continue;
852+
}
853+
854+
const auto Directive = RefinePreprocessorDirective(Token->Literal);
855+
856+
if (Directive == "if" ||
857+
Directive == "ifdef" ||
858+
Directive == "ifndef")
859+
{
860+
++PreprocessorScopeLevel;
861+
}
862+
else if (Directive == "endif")
863+
{
864+
if (PreprocessorScopeLevel > 0)
865+
{
866+
--PreprocessorScopeLevel;
867+
}
868+
else
869+
{
870+
LOG_ERROR_MESSAGE("No matching #if directive\n", PrintTokenContext(Token, 4));
871+
}
872+
}
873+
else if (Directive == "define")
874+
{
875+
// #define MACRO
876+
// ^
877+
878+
// Only process macros in the global scope as we don't
879+
// handle conditional compilation
880+
if (PreprocessorScopeLevel == 0)
881+
{
882+
auto MacroNameToken = Token;
883+
++MacroNameToken;
884+
// #define MACRO
885+
// ^
886+
if (MacroNameToken != m_Tokens.end() &&
887+
// The name should be an identifier
888+
MacroNameToken->Type == TokenType::Identifier &&
889+
// Check that the name is on the same line
890+
MacroNameToken->Delimiter.find_first_of("\r\n") == std::string::npos)
891+
{
892+
m_PreprocessorDefinitions.emplace(MacroNameToken->Literal, Token);
893+
}
894+
}
895+
}
896+
897+
++Token;
898+
}
899+
900+
if (PreprocessorScopeLevel > 0)
901+
{
902+
LOG_ERROR_MESSAGE("Missing #endif directive at the end of the file. Current preprocessor scope level: ", PreprocessorScopeLevel);
903+
}
904+
}
830905

831906
// The function replaces cbuffer with uniform and adds semicolon if it is missing after the closing brace:
832907
// cbuffer
@@ -2270,60 +2345,63 @@ void HLSL2GLSLConverterImpl::ConversionStream::ParseShaderParameter(TokenListTyp
22702345
auto TypeToken = Token;
22712346
ParamInfo.Type = Token->Literal;
22722347

2273-
++Token;
2274-
// out float4 Color : SV_Target,
2275-
// ^
2276-
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF while parsing argument list");
2277-
VERIFY_PARSER_STATE(Token, Token->Type == TokenType::Identifier, "Missing argument name after ", ParamInfo.Type);
2278-
ParamInfo.Name = Token->Literal;
2279-
2280-
++Token;
2281-
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF");
2282-
2283-
if (Token->Type == TokenType::OpenSquareBracket)
2348+
if (ParamInfo.storageQualifier != ShaderParameterInfo::StorageQualifier::Ret)
22842349
{
2285-
// triangle VSOut In[3]
2286-
// ^
2287-
ProcessScope(
2288-
Token, m_Tokens.end(), TokenType::OpenSquareBracket, TokenType::ClosingSquareBracket,
2289-
[&](TokenListType::iterator& tkn, int) {
2290-
ParamInfo.ArraySize.append(tkn->Delimiter);
2291-
ParamInfo.ArraySize.append(tkn->Literal);
2292-
++tkn;
2293-
} //
2294-
);
2295-
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF");
2296-
// triangle VSOut In[3],
2297-
// ^
2298-
VERIFY_PARSER_STATE(Token, Token->Type == TokenType::ClosingSquareBracket, "Closing staple expected");
2350+
++Token;
2351+
// out float4 Color : SV_Target,
2352+
// ^
2353+
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF while parsing argument list");
2354+
VERIFY_PARSER_STATE(Token, Token->Type == TokenType::Identifier, "Missing argument name after ", ParamInfo.Type);
2355+
ParamInfo.Name = Token->Literal;
22992356

23002357
++Token;
23012358
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF");
2302-
VERIFY_PARSER_STATE(Token, Token->Type != TokenType::OpenSquareBracket, "Multi-dimensional arrays are not supported");
2303-
}
23042359

2305-
2306-
if (TypeToken->IsBuiltInType())
2307-
{
2308-
// out float4 Color : SV_Target,
2309-
// ^
2310-
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected end of file after argument \"", ParamInfo.Name, '\"');
2311-
if (Token->Literal == ":")
2360+
if (Token->Type == TokenType::OpenSquareBracket)
23122361
{
2313-
++Token;
2314-
// out float4 Color : SV_Target,
2315-
// ^
2316-
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected end of file while looking for semantic for argument \"", ParamInfo.Name, '\"');
2317-
VERIFY_PARSER_STATE(Token, Token->Type == TokenType::Identifier, "Missing semantic for argument \"", ParamInfo.Name, '\"');
2318-
// Transform to lower case - semantics are case-insensitive
2319-
ParamInfo.Semantic = StrToLower(Token->Literal);
2362+
// triangle VSOut In[3]
2363+
// ^
2364+
ProcessScope(
2365+
Token, m_Tokens.end(), TokenType::OpenSquareBracket, TokenType::ClosingSquareBracket,
2366+
[&](TokenListType::iterator& tkn, int) {
2367+
ParamInfo.ArraySize.append(tkn->Delimiter);
2368+
ParamInfo.ArraySize.append(tkn->Literal);
2369+
++tkn;
2370+
} //
2371+
);
2372+
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF");
2373+
// triangle VSOut In[3],
2374+
// ^
2375+
VERIFY_PARSER_STATE(Token, Token->Type == TokenType::ClosingSquareBracket, "Closing staple expected");
23202376

23212377
++Token;
2378+
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected EOF");
2379+
VERIFY_PARSER_STATE(Token, Token->Type != TokenType::OpenSquareBracket, "Multi-dimensional arrays are not supported");
2380+
}
2381+
2382+
if (TypeToken->IsBuiltInType())
2383+
{
23222384
// out float4 Color : SV_Target,
2323-
// ^
2385+
// ^
2386+
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected end of file after argument \"", ParamInfo.Name, '\"');
2387+
if (Token->Literal == ":")
2388+
{
2389+
++Token;
2390+
// out float4 Color : SV_Target,
2391+
// ^
2392+
VERIFY_PARSER_STATE(Token, Token != m_Tokens.end(), "Unexpected end of file while looking for semantic for argument \"", ParamInfo.Name, '\"');
2393+
VERIFY_PARSER_STATE(Token, Token->Type == TokenType::Identifier, "Missing semantic for argument \"", ParamInfo.Name, '\"');
2394+
// Transform to lower case - semantics are case-insensitive
2395+
ParamInfo.Semantic = StrToLower(Token->Literal);
2396+
2397+
++Token;
2398+
// out float4 Color : SV_Target,
2399+
// ^
2400+
}
23242401
}
23252402
}
2326-
else
2403+
2404+
if (!TypeToken->IsBuiltInType())
23272405
{
23282406
const auto& StructName = TypeToken->Literal;
23292407
auto it = m_StructDefinitions.find(StructName.c_str());
@@ -2349,8 +2427,11 @@ void HLSL2GLSLConverterImpl::ConversionStream::ParseShaderParameter(TokenListTyp
23492427
while (TypeToken != m_Tokens.end() && TypeToken->Type != TokenType::ClosingBrace)
23502428
{
23512429
ShaderParameterInfo MemberInfo;
2352-
MemberInfo.storageQualifier = ParamInfo.storageQualifier;
23532430
ParseShaderParameter(TypeToken, MemberInfo);
2431+
// Set storage qualifier after we process the member as otherwise
2432+
// members of a struct with the StorageQualifier::Ret qualifier
2433+
// will not be processed.
2434+
MemberInfo.storageQualifier = ParamInfo.storageQualifier;
23542435
ParamInfo.members.emplace_back(std::move(MemberInfo));
23552436
// struct VSOutput
23562437
// {
@@ -2376,11 +2457,48 @@ void HLSL2GLSLConverterImpl::ConversionStream::ProcessFunctionParameters(TokenLi
23762457
// ^
23772458
auto FuncNameToken = Token;
23782459

2379-
bIsVoid = TypeToken->Type == TokenType::kw_void;
2460+
auto ActualTypeToken = TypeToken;
2461+
{
2462+
// PS_OUTPUT TestPS ( in VSOutput In,
2463+
2464+
auto define_it = m_PreprocessorDefinitions.find(ActualTypeToken->Literal.c_str());
2465+
if (define_it != m_PreprocessorDefinitions.end())
2466+
{
2467+
auto DefinedTypeToken = define_it->second;
2468+
// #define PS_OUTPUT PSOutput
2469+
// ^
2470+
// DefinedTypeToken
2471+
2472+
// Check that the define directive is before the type token
2473+
if (DefinedTypeToken->Idx < TypeToken->Idx)
2474+
{
2475+
++DefinedTypeToken;
2476+
// #define PS_OUTPUT PSOutput
2477+
// ^
2478+
// DefinedTypeToken
2479+
if (DefinedTypeToken != m_Tokens.end() && DefinedTypeToken->Literal == TypeToken->Literal)
2480+
{
2481+
++DefinedTypeToken;
2482+
// #define PS_OUTPUT PSOutput
2483+
// ^
2484+
// DefinedTypeToken
2485+
if (DefinedTypeToken != m_Tokens.end() &&
2486+
// Check that the definition is on the same line (we don't handle backslash at the moment)
2487+
DefinedTypeToken->Delimiter.find_first_of("\r\n") == std::string::npos &&
2488+
(DefinedTypeToken->IsBuiltInType() || DefinedTypeToken->Type == TokenType::Identifier))
2489+
{
2490+
ActualTypeToken = DefinedTypeToken;
2491+
}
2492+
}
2493+
}
2494+
}
2495+
}
2496+
2497+
bIsVoid = ActualTypeToken->Type == TokenType::kw_void;
23802498
if (!bIsVoid)
23812499
{
23822500
ShaderParameterInfo RetParam;
2383-
RetParam.Type = TypeToken->Literal;
2501+
RetParam.Type = ActualTypeToken->Literal;
23842502
RetParam.Name = FuncNameToken->Literal;
23852503
RetParam.storageQualifier = ShaderParameterInfo::StorageQualifier::Ret;
23862504
Params.emplace_back(std::move(RetParam));
@@ -2637,7 +2755,7 @@ void HLSL2GLSLConverterImpl::ConversionStream::ProcessFunctionParameters(TokenLi
26372755
else
26382756
{
26392757
// VSOut TestVS ()
2640-
auto TmpTypeToken = TypeToken;
2758+
auto TmpTypeToken = ActualTypeToken;
26412759
ParseShaderParameter(TmpTypeToken, RetParam);
26422760
}
26432761
TypeToken->Type = TokenType::Identifier;
@@ -4482,6 +4600,8 @@ String HLSL2GLSLConverterImpl::ConversionStream::Convert(const Char* EntryPoint,
44824600
}
44834601
}
44844602

4603+
ParseGlobalPreprocessorDefines();
4604+
44854605
auto ShaderEntryPointToken = m_Tokens.end();
44864606
// Process textures and search for the shader entry point.
44874607
// GLSL does not allow local variables of sampler type, so the
@@ -4665,6 +4785,7 @@ String HLSL2GLSLConverterImpl::ConversionStream::Convert(const Char* EntryPoint,
46654785
{
46664786
m_Tokens.swap(TokensCopy);
46674787
m_StructDefinitions.clear();
4788+
m_PreprocessorDefinitions.clear();
46684789
m_Objects.clear();
46694790
}
46704791

0 commit comments

Comments
 (0)