You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

951 lines
33 KiB

  1. unit uengShaderGeneratorArgs;
  2. {$mode objfpc}{$H+}
  3. {$I uengShaderFile.inc}
  4. interface
  5. uses
  6. Classes, SysUtils,
  7. uengShaderFileTypes, uengShaderPart
  8. {$IFDEF SHADER_FILE_USE_BITSPACE_UTILS}
  9. , uutlGenerics
  10. {$ELSE}
  11. , uengShaderFileGenerics
  12. {$ENDIF}
  13. ;
  14. type
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  16. TengGenerateFlag = (
  17. gfGenerateProcedureMain, // generate main procedure code
  18. gfGenerateProcedureCode, // generate procedure code
  19. gfGenerateProcedureCall, // generate procedure call
  20. gfGenerateInlineCode, // generate procedure as inline code
  21. gfGenerateParameterCode, // generate code for parameter items
  22. gfAddProcedureItem, // add procedure item to generator args
  23. gfAddParameterItem // add parameter items to generator args
  24. );
  25. TengGenerateFlags = set of TengGenerateFlag;
  26. TengGenerateFlagsStack = specialize TutlSimpleList<TengGenerateFlags>;
  27. TengPopCodeFlag = (
  28. pcfAppend,
  29. pcfPrepend,
  30. pcfAddEmptyLine
  31. );
  32. TengPopCodeFlags = set of TengPopCodeFlag;
  33. TengShaderGeneratorArgs = class(TObject)
  34. private type
  35. TengGeneratorToken = (
  36. gtNormal = 0, // normal text
  37. gtLineBreak = 1, // line break
  38. gtCommandEnd = 2, // command end (like ';')
  39. gtBlockBegin = 3, // code block begin (to calculate indent)
  40. gtBlockEnd = 4, // code block end (to calculate indent)
  41. gtAppendToPrev = 5, // append current line to prev line
  42. gtIgnoreNextCommandEnd = 6, // ignore next command end
  43. gtToken = 7 // code token (like '$INCLUDE' or '$IF')
  44. );
  45. TCodePart = class(TObject)
  46. private
  47. fText: String;
  48. fIndent: Integer;
  49. fToken: TengGeneratorToken;
  50. function GetDebugText: String;
  51. function GetCode: String;
  52. public
  53. property Text: String read fText write fText;
  54. property Code: String read GetCode;
  55. property DebugText: String read GetDebugText;
  56. property Indent: Integer read fIndent;
  57. property Token: TengGeneratorToken read fToken;
  58. constructor Create(const aToken: TengGeneratorToken; const aText: String; const aIndent: Integer = High(Integer));
  59. end;
  60. TCodePartList = specialize TutlSimpleList<TCodePart>;
  61. public type
  62. TCodeStackItem = class(TObject)
  63. private
  64. fItems: TCodePartList;
  65. function GetIsEmpty: Boolean;
  66. public
  67. property Items: TCodePartList read fItems;
  68. property IsEmpty: Boolean read GetIsEmpty;
  69. procedure GenerateCode(const aCode: TengShaderCode);
  70. procedure Merge(const aItem: TCodeStackItem; aIndex: Integer);
  71. constructor Create;
  72. destructor Destroy; override;
  73. end;
  74. private type
  75. TCodeStack = specialize TutlSimpleList<TCodeStackItem>;
  76. TParameterMap = specialize TutlMap<string, TengShaderPart>;
  77. TProcedureList = specialize TutlList<TengShaderPart>;
  78. TProcParamStack = specialize TutlSimpleList<TStrings>;
  79. private
  80. fInlineReturnCounter: Integer;
  81. fCode: TCodeStack;
  82. fRoot: TengShaderPart;
  83. fFlagStack: TengGenerateFlagsStack;
  84. fMetaDataList: TengMetaDataList;
  85. fParameters: TParameterMap;
  86. fProcedures: TProcedureList;
  87. fProcParams: TProcParamStack;
  88. fMaxParameterLength: Integer;
  89. function GetFlags: TengGenerateFlags;
  90. function GetProcParams: TStrings;
  91. public
  92. property Root: TengShaderPart read fRoot;
  93. property Flags: TengGenerateFlags read GetFlags;
  94. property ProcParams: TStrings read GetProcParams;
  95. property MaxParameterLength: Integer read fMaxParameterLength;
  96. function PushCode: TengShaderGeneratorArgs;
  97. function PushFlags(const aFlags: TengGenerateFlags): TengShaderGeneratorArgs;
  98. function PushProcParams(const aParams: TStrings): TengShaderGeneratorArgs;
  99. function PopCode(const aFlags: TengPopCodeFlags): TengShaderGeneratorArgs;
  100. function PopFlags: TengShaderGeneratorArgs;
  101. function PopProcParams: TengShaderGeneratorArgs;
  102. function AddText(const aText: String): TengShaderGeneratorArgs;
  103. function AddToken(const aToken: String): TengShaderGeneratorArgs;
  104. function AddCommandEnd(const aToken: String): TengShaderGeneratorArgs;
  105. function AddLineBreak: TengShaderGeneratorArgs;
  106. function BeginBlock(const aIndent: Integer = High(Integer)): TengShaderGeneratorArgs;
  107. function EndBlock(const aCanAppend: Boolean = false): TengShaderGeneratorArgs;
  108. function AppendToPrevLine: TengShaderGeneratorArgs;
  109. function IgnoreNextCommandEnd: TengShaderGeneratorArgs;
  110. procedure AddMeta(const aMeta: TengMetaData);
  111. procedure AddParameter(const aParam: TengShaderPart);
  112. procedure AddProcedure(const aProc: TengShaderPart);
  113. function ExtractCurrentCommand(const aCommand: TCodeStackItem): Integer;
  114. function ReplaceIdents(const aOld, aNew: TStrings): TengShaderGeneratorArgs;
  115. function ReplaceReturns(const aCommand: TCodeStackItem; const aRetType, aName: String): TengShaderGeneratorArgs;
  116. procedure GenerateParameterCode(const aTypes: CengShaderPartArr);
  117. procedure GenerateProcedureCode;
  118. procedure GenerateMetaCode;
  119. procedure GenerateCode(const aCode: TengShaderCode);
  120. constructor Create(const aRoot: TengShaderPart);
  121. destructor Destroy; override;
  122. end;
  123. implementation
  124. uses
  125. Math, RegExpr,
  126. uengShaderFileHelper, uengShaderFileConstants, uengShaderPartParameter, uengShaderPartProc;
  127. const
  128. WHITESPACES = [' ', #9];
  129. type
  130. TCodeBlock = class(TStringList)
  131. private
  132. function GetLast: String;
  133. function GetDepth(const aIndex: Integer): Integer;
  134. procedure SetLast(aValue: String);
  135. procedure SetDepth(const aIndex: Integer; aValue: Integer);
  136. public
  137. property Last: String read GetLast write SetLast;
  138. property Depth[const aIndex: Integer]: Integer read GetDepth write SetDepth;
  139. function Add(const aString: String; const aDepth: Integer): Integer; reintroduce;
  140. procedure Clear; override;
  141. constructor Create;
  142. end;
  143. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  144. //TCodeBlock////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  145. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  146. function TCodeBlock.GetLast: String;
  147. begin
  148. result := Get(Count-1);
  149. end;
  150. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  151. function TCodeBlock.GetDepth(const aIndex: Integer): Integer;
  152. begin
  153. result := PtrInt(Objects[aIndex]);
  154. end;
  155. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  156. procedure TCodeBlock.SetLast(aValue: String);
  157. begin
  158. Put(Count-1, aValue);
  159. end;
  160. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. procedure TCodeBlock.SetDepth(const aIndex: Integer; aValue: Integer);
  162. begin
  163. Objects[aIndex] := TObject(PtrInt(aValue));
  164. end;
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. function TCodeBlock.Add(const aString: String; const aDepth: Integer): Integer;
  167. begin
  168. result := inherited AddObject(aString, TObject(PtrInt(aDepth)));
  169. end;
  170. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  171. procedure TCodeBlock.Clear;
  172. begin
  173. inherited Clear;
  174. Add('', 0);
  175. end;
  176. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  177. constructor TCodeBlock.Create;
  178. begin
  179. inherited Create;
  180. Clear;
  181. end;
  182. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  183. //TengShaderGeneratorArgs.TCodePart///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  184. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  185. function TengShaderGeneratorArgs.TCodePart.GetCode: String;
  186. begin
  187. case fToken of
  188. gtNormal,
  189. gtCommandEnd:
  190. result := fText;
  191. gtLineBreak:
  192. result := sLineBreak;
  193. else
  194. result := '';
  195. end;
  196. end;
  197. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  198. function TengShaderGeneratorArgs.TCodePart.GetDebugText: String;
  199. begin
  200. case fToken of
  201. gtNormal: result := '[N]' + fText;
  202. gtLineBreak: result := sLineBreak;
  203. gtCommandEnd: result := '[C]' + fText;
  204. gtBlockBegin: if (fIndent = High(Integer))
  205. then result := '[B]'
  206. else result := format('[B%d]', [fIndent]);
  207. gtBlockEnd: result := '[E]';
  208. gtToken: result := '[T' + fText + ']';
  209. gtAppendToPrev: result := '[A]';
  210. else
  211. result := '[' + IntToStr(Integer(fToken)) + ']' + fText
  212. end;
  213. end;
  214. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  215. constructor TengShaderGeneratorArgs.TCodePart.Create(const aToken: TengGeneratorToken; const aText: String; const aIndent: Integer);
  216. begin
  217. inherited Create;
  218. fToken := aToken;
  219. fText := aText;
  220. fIndent := aIndent;
  221. end;
  222. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  223. //TengShaderGeneratorArgs.TCodeStackItem//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  225. function TengShaderGeneratorArgs.TCodeStackItem.GetIsEmpty: Boolean;
  226. begin
  227. result := (fItems.Count = 0);
  228. end;
  229. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  230. procedure TengShaderGeneratorArgs.TCodeStackItem.GenerateCode(const aCode: TengShaderCode);
  231. type
  232. TGenFlag = (
  233. gfToken, // current line has a token in it
  234. gfTokenOnly, // current line has only a token (or whitespaces) in it
  235. gfPrevIsEmpty, // previouse line was empty (or whitespaces only)
  236. gfAddToPrev, // add current line to previouse line
  237. gfIgnoreNextCommandEnd
  238. );
  239. TGenFlags = set of TGenFlag;
  240. var
  241. i: Integer;
  242. f: TGenFlags;
  243. cb: TCodeBlock;
  244. {$IFDEF SHADER_FILE_DEBUG}
  245. procedure GenerateDebugCode;
  246. var
  247. cp: TCodePart;
  248. s: String;
  249. begin
  250. s := '';
  251. for cp in fItems do
  252. s := s + cp.DebugText;
  253. aCode.Text := aCode.Text + s + sLineBreak + sLineBreak;
  254. end;
  255. procedure GenerateCurrentCode(const aHeader: String);
  256. var
  257. i: Integer;
  258. begin
  259. aCode.Add(aHeader);
  260. for i := 0 to cb.Count-1 do
  261. aCode.Add(Format('[%02d]%s|', [cb.Depth[i], cb[i]]));
  262. aCode.Add('');
  263. aCode.Add('');
  264. aCode.Add('');
  265. end;
  266. {$ENDIF}
  267. function GetCurrentIndent(const aStr: String; const aIgnoreEmptyLines: Boolean): Integer;
  268. var
  269. len: Integer;
  270. begin
  271. if (Trim(aStr) <> '') or not aIgnoreEmptyLines then begin
  272. result := 1;
  273. len := Length(aStr);
  274. while (result <= len) and (aStr[result] in WHITESPACES) do
  275. inc(result);
  276. dec(result);
  277. end else
  278. result := High(Integer);
  279. end;
  280. function IndentStr(const aStr: String; aIndent: Integer): String;
  281. var
  282. i, l: Integer;
  283. begin
  284. if (aStr = '') then
  285. aIndent := 0;
  286. if (aIndent < 0) then begin
  287. i := 1;
  288. l := Length(aStr);
  289. while (i <= l) and (i <= -aIndent) and (aStr[i] in WHITESPACES) do
  290. inc(i);
  291. result := copy(aStr, i, l - i + 1);
  292. end else if (aIndent > 0) then
  293. result := StringOfChar(' ', aIndent) + aStr
  294. else
  295. result := aStr;
  296. end;
  297. procedure IndentBlock(aDepth, aAbsIndent: Integer);
  298. var
  299. i, indent, minCurIndent: Integer;
  300. begin
  301. i := cb.Count-1;
  302. minCurIndent := High(Integer);
  303. while (i >= 0) and (cb.Depth[i] = aDepth) do begin
  304. minCurIndent := min(minCurIndent, GetCurrentIndent(cb[i], true));
  305. dec(i);
  306. end;
  307. inc(i);
  308. indent := aAbsIndent - minCurIndent;
  309. while (i < cb.Count) do begin
  310. cb[i] := IndentStr(cb[i], indent);
  311. cb.Depth[i] := cb.Depth[i] - 1;
  312. inc(i);
  313. end;
  314. end;
  315. procedure ProgressBlock(const aCurrentBlockIndent, aDepth: Integer);
  316. var
  317. cp: TCodePart;
  318. tmp: Integer;
  319. s: String;
  320. begin
  321. while (i < fItems.Count) do begin
  322. cp := fItems[i];
  323. inc(i);
  324. if (Trim(cb.Last) = '') then
  325. cb.Depth[cb.Count-1] := aDepth;
  326. case cp.Token of
  327. gtLineBreak: begin
  328. if (Trim(cb.Last) = '') then begin
  329. if (f * [gfTokenOnly, gfPrevIsEmpty] = []) then begin
  330. Include(f, gfPrevIsEmpty);
  331. cb.Add('', aDepth);
  332. end else
  333. cb.Last := '';
  334. end else begin
  335. if (gfAddToPrev in f) and (cb.Count >= 2) then begin
  336. cb[cb.Count-2] := cb[cb.Count-2] + TrimLeft(cb.Last);
  337. cb.Last := '';
  338. end else
  339. cb.Add('', aDepth);
  340. Exclude(f, gfPrevIsEmpty);
  341. end;
  342. f := f - [gfToken, gfTokenOnly, gfAddToPrev];
  343. end;
  344. gtToken: begin
  345. Include(f, gfToken);
  346. if (Trim(cb.Last) = '') then
  347. Include(f, gfTokenOnly);
  348. end;
  349. gtBlockBegin: begin
  350. Include(f, gfPrevIsEmpty);
  351. tmp := GetCurrentIndent(cb.Last, false);
  352. if (tmp = High(Integer)) then
  353. tmp := 0;
  354. if (Trim(cb.Last) <> '') then
  355. inc(tmp, 4);
  356. if (cp.Indent <> High(Integer)) then
  357. inc(tmp, cp.Indent);
  358. tmp := max(tmp, aCurrentBlockIndent);
  359. ProgressBlock(tmp, aDepth + 1);
  360. end;
  361. gtBlockEnd: begin
  362. {$IFDEF SHADER_FILE_DEBUG}
  363. GenerateCurrentCode(Format('------====== DEBUG STEP BEFORE INDENT (%d) ======------', [aCurrentBlockIndent]));
  364. {$ENDIF}
  365. IndentBlock(aDepth, aCurrentBlockIndent);
  366. {$IFDEF SHADER_FILE_DEBUG}
  367. GenerateCurrentCode(Format('------====== DEBUG STEP AFTER INDENT (%d) ======------', [aCurrentBlockIndent]));
  368. {$ENDIF}
  369. exit;
  370. end;
  371. gtIgnoreNextCommandEnd: begin
  372. include(f, gfIgnoreNextCommandEnd);
  373. end;
  374. gtAppendToPrev: begin
  375. if (Trim(cb.Last) = '') and not (gfPrevIsEmpty in f) then
  376. include(f, gfAddToPrev);
  377. end;
  378. gtCommandEnd: begin
  379. if (gfIgnoreNextCommandEnd in f)
  380. then exclude(f, gfIgnoreNextCommandEnd)
  381. else cb.Last := cb.Last + cp.Code;
  382. end;
  383. else
  384. s := cp.Code;
  385. if (Trim(s) <> '') then
  386. exclude(f, gfIgnoreNextCommandEnd);
  387. cb.Last := cb.Last + s;
  388. end;
  389. end;
  390. end;
  391. var
  392. s, e: Integer;
  393. begin
  394. {$IFDEF SHADER_FILE_DEBUG}
  395. GenerateDebugCode;
  396. {$ENDIF}
  397. i := 0;
  398. f := [gfPrevIsEmpty];
  399. cb := TCodeBlock.Create;
  400. try
  401. ProgressBlock(0, 0);
  402. s := 0;
  403. e := cb.Count-1;
  404. while (s < cb.Count) and (Trim(cb[s]) = '') do
  405. inc(s);
  406. while (e >= 0) and (Trim(cb[e]) = '') do
  407. dec(e);
  408. for i := s to e do
  409. aCode.Add(
  410. {$IFDEF SHADER_FILE_DEBUG}
  411. Format('[%02d]%s|', [cb.Depth[i], cb[i]])
  412. {$ELSE}
  413. cb[i]
  414. {$ENDIF}
  415. );
  416. finally
  417. FreeAndNil(cb);
  418. end;
  419. end;
  420. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  421. procedure TengShaderGeneratorArgs.TCodeStackItem.Merge(const aItem: TCodeStackItem; aIndex: Integer);
  422. begin
  423. if (aIndex < 0) then
  424. aIndex := 0;
  425. if (aIndex > fItems.Count) then
  426. aIndex := fItems.Count;
  427. while (aItem.Items.Count > 0) do
  428. fItems.Insert(aIndex, aItem.Items.PopLast(false));
  429. end;
  430. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  431. constructor TengShaderGeneratorArgs.TCodeStackItem.Create;
  432. begin
  433. inherited Create;
  434. fItems := TCodePartList.Create(true);
  435. end;
  436. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  437. destructor TengShaderGeneratorArgs.TCodeStackItem.Destroy;
  438. begin
  439. FreeAndNil(fItems);
  440. inherited Destroy;
  441. end;
  442. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  443. //TengShaderGeneratorArgs///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  444. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  445. function TengShaderGeneratorArgs.GetFlags: TengGenerateFlags;
  446. begin
  447. result := fFlagStack.Last;
  448. end;
  449. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  450. function TengShaderGeneratorArgs.GetProcParams: TStrings;
  451. begin
  452. result := fProcParams.Last;
  453. end;
  454. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  455. function TengShaderGeneratorArgs.PushCode: TengShaderGeneratorArgs;
  456. begin
  457. fCode.PushLast(TCodeStackItem.Create);
  458. result := self;
  459. end;
  460. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  461. function TengShaderGeneratorArgs.PushFlags(const aFlags: TengGenerateFlags): TengShaderGeneratorArgs;
  462. begin
  463. fFlagStack.PushLast(aFlags);
  464. result := self;
  465. end;
  466. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  467. function TengShaderGeneratorArgs.PushProcParams(const aParams: TStrings): TengShaderGeneratorArgs;
  468. begin
  469. fProcParams.PushLast(aParams);
  470. result := self;
  471. end;
  472. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  473. function TengShaderGeneratorArgs.PopCode(const aFlags: TengPopCodeFlags): TengShaderGeneratorArgs;
  474. var
  475. csi: TCodeStackItem;
  476. begin
  477. csi := fCode.PopLast(false);
  478. try
  479. if csi.IsEmpty then
  480. exit;
  481. if (pcfPrepend in aFlags) then begin
  482. if (pcfAddEmptyLine in aFlags) then
  483. csi.Items.Add(TCodePart.Create(gtLineBreak, ''));
  484. fCode.Last.Merge(csi, 1);
  485. end else if (pcfAppend in aFlags) then begin
  486. if (pcfAddEmptyLine in aFlags) then
  487. fCode.Last.Items.Add(TCodePart.Create(gtLineBreak, ''));
  488. fCode.Last.Merge(csi, fCode.Last.Items.Count);
  489. end;
  490. finally
  491. FreeAndNil(csi);
  492. end;
  493. result := self;
  494. end;
  495. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  496. function TengShaderGeneratorArgs.PopFlags: TengShaderGeneratorArgs;
  497. begin
  498. fFlagStack.PopLast(true);
  499. result := self;
  500. end;
  501. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  502. function TengShaderGeneratorArgs.PopProcParams: TengShaderGeneratorArgs;
  503. begin
  504. fProcParams.PopLast;
  505. result := self;
  506. end;
  507. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  508. function TengShaderGeneratorArgs.AddText(const aText: String): TengShaderGeneratorArgs;
  509. begin
  510. fCode.Last.Items.Add(TCodePart.Create(gtNormal, aText));
  511. result := self;
  512. end;
  513. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  514. function TengShaderGeneratorArgs.AddToken(const aToken: String): TengShaderGeneratorArgs;
  515. begin
  516. fCode.Last.Items.Add(TCodePart.Create(gtToken, aToken));
  517. result := self;
  518. end;
  519. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  520. function TengShaderGeneratorArgs.AddCommandEnd(const aToken: String): TengShaderGeneratorArgs;
  521. begin
  522. fCode.Last.Items.Add(TCodePart.Create(gtCommandEnd, aToken));
  523. result := self;
  524. end;
  525. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  526. function TengShaderGeneratorArgs.AddLineBreak: TengShaderGeneratorArgs;
  527. begin
  528. fCode.Last.Items.Add(TCodePart.Create(gtLineBreak, ''));
  529. result := self;
  530. end;
  531. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  532. function TengShaderGeneratorArgs.BeginBlock(const aIndent: Integer): TengShaderGeneratorArgs;
  533. begin
  534. fCode.Last.Items.Add(TCodePart.Create(gtBlockBegin, '', aIndent));
  535. result := self;
  536. end;
  537. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  538. function TengShaderGeneratorArgs.EndBlock(const aCanAppend: Boolean): TengShaderGeneratorArgs;
  539. begin
  540. fCode.Last.Items.Add(TCodePart.Create(gtBlockEnd, ''));
  541. result := self;
  542. end;
  543. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  544. function TengShaderGeneratorArgs.AppendToPrevLine: TengShaderGeneratorArgs;
  545. begin
  546. fCode.Last.Items.Add(TCodePart.Create(gtAppendToPrev, ''));
  547. result := self;
  548. end;
  549. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  550. function TengShaderGeneratorArgs.IgnoreNextCommandEnd: TengShaderGeneratorArgs;
  551. begin
  552. fCode.Last.Items.Add(TCodePart.Create(gtIgnoreNextCommandEnd, ''));
  553. result := self;
  554. end;
  555. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  556. procedure TengShaderGeneratorArgs.AddMeta(const aMeta: TengMetaData);
  557. begin
  558. fMetaDataList.Add(aMeta);
  559. end;
  560. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  561. procedure TengShaderGeneratorArgs.AddParameter(const aParam: TengShaderPart);
  562. var
  563. p: TengShaderPart;
  564. s: String;
  565. begin
  566. if not (aParam is TengShaderPartParameter) then
  567. raise EengShaderPartInternal.Create('parameter (' + aParam.ClassName + ') is not a ' + TengShaderPartParameter.ClassName, aParam);
  568. with (aParam as TengShaderPartParameter) do begin
  569. p := fParameters[Name];
  570. if Assigned(p) and (p <> aParam) then begin
  571. s := Format('use of duplicate identifier: %s (%s %d:%d)', [Name, Filename, Line + 1, Col]) + sLineBreak +
  572. 'previously declared here:' + sLineBreak +
  573. Format(' %s %d:%d', [p.Filename, p.Line + 1, p.Col]) + sLineBreak;
  574. fRoot.LogMsg(llWarning, s);
  575. fParameters[Name] := aParam;
  576. end else if (p <> aParam) then
  577. fParameters.Add(Name, aParam);
  578. end;
  579. end;
  580. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  581. procedure TengShaderGeneratorArgs.AddProcedure(const aProc: TengShaderPart);
  582. begin
  583. if not (aProc is TengShaderPartProc) then
  584. raise EengShaderPartInternal.Create('parameter (' + aProc.ClassName + ') is not a ' + TengShaderPartProc.ClassName, aProc);
  585. if (fProcedures.IndexOf(aProc) < 0) then
  586. fProcedures.Add(aProc);
  587. end;
  588. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  589. function TengShaderGeneratorArgs.ExtractCurrentCommand(const aCommand: TCodeStackItem): Integer;
  590. var
  591. csi: TCodeStackItem;
  592. i, len: Integer;
  593. s: String;
  594. begin
  595. csi := fCode.Last;
  596. result := 0;
  597. if not Assigned(aCommand) then
  598. exit;
  599. // find last command end token
  600. while (csi.Items.Last.Token <> gtCommandEnd) do
  601. aCommand.Items.PushFirst(csi.Items.PopLast(false));
  602. // move forward to first code part with text
  603. while (aCommand.Items.First.Token <> gtNormal) or (Trim(aCommand.Items.First.Text) = '') do
  604. csi.Items.PushLast(aCommand.Items.PopFirst(false));
  605. // extract leading whitespaces
  606. i := 1;
  607. s := aCommand.Items.First.Text;
  608. len := Length(s);
  609. while (s[i] in WHITESPACES) and (i <= len) do
  610. inc(i);
  611. csi.Items.PushLast(TCodePart.Create(gtNormal, Copy(s, 1, i-1)));
  612. aCommand.Items.First.Text := copy(s, i, len-i+1);
  613. result := i - 1;
  614. end;
  615. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  616. function TengShaderGeneratorArgs.ReplaceIdents(const aOld, aNew: TStrings): TengShaderGeneratorArgs;
  617. var
  618. rx: TRegExpr;
  619. i: Integer;
  620. cp: TCodePart;
  621. begin
  622. if (aOld.Count <> aNew.Count) then
  623. raise EengShaderPartInternal.Create('old and new ident must have the same size');
  624. rx := TRegExpr.Create;
  625. try
  626. for i := 0 to aOld.Count-1 do begin
  627. rx.Expression := '([^A-z0-9_]+|^)' + aOld[i] + '([^A-z0-9_]+|$)';
  628. for cp in fCode.Last.Items do
  629. cp.Text := rx.Replace(cp.Text, '$1' + aNew[i] + '$2', true);
  630. end;
  631. finally
  632. FreeAndNil(rx);
  633. end;
  634. result := self;
  635. end;
  636. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  637. function TengShaderGeneratorArgs.ReplaceReturns(const aCommand: TCodeStackItem; const aRetType, aName: String): TengShaderGeneratorArgs;
  638. var
  639. rx: TRegExpr;
  640. RetCount, i, j: Integer;
  641. csi: TCodeStackItem;
  642. cp: TCodePart;
  643. s: String;
  644. begin
  645. rx := TRegExpr.Create;
  646. try
  647. rx.Expression := '^(.*?\s+)return\s*(.*)$';
  648. csi := fCode.Last;
  649. // find number of "return" in code
  650. RetCount := 0;
  651. for cp in csi.Items do begin
  652. s := cp.Code;
  653. if rx.Exec(s) then
  654. inc(RetCount);
  655. end;
  656. // no "return" found
  657. if (RetCount = 0) then begin
  658. raise EengShaderPartInternal.Create('expected "return" token in function');
  659. // more than one "return"
  660. end else if (RetCount > 1) then begin
  661. // find block begin
  662. i := 0;
  663. while (csi.Items[i].Token <> gtBlockBegin) and (i < csi.Items.Count) do
  664. inc(i);
  665. if (i < csi.Items.Count)
  666. then inc(i)
  667. else i := 0;
  668. // insert temp variable
  669. s := Format('%s_ret%.4d', [aName, fInlineReturnCounter]);
  670. inc(fInlineReturnCounter);
  671. csi.Items.Insert(i+0, TCodePart.Create(gtNormal, aRetType + ' ' + s));
  672. csi.Items.Insert(i+1, TCodePart.Create(gtCommandEnd, ';'));
  673. csi.Items.Insert(i+2, TCodePart.Create(gtLineBreak, ''));
  674. // replace "return" with temp variable
  675. for cp in csi.Items do
  676. cp.Text := rx.Replace(cp.Text, '$1' + s + ' = $2', true);
  677. // merge code
  678. csi.Merge(aCommand, csi.Items.Count);
  679. AddText(s);
  680. // exact one "return"
  681. end else begin
  682. i := csi.Items.Count-1;
  683. while (i > 0) do begin
  684. cp := csi.Items[i];
  685. if rx.Exec(cp.Text) then begin
  686. csi.Items.Insert(i, TCodePart.Create(gtNormal, rx.Match[1]));
  687. cp.Text := rx.Replace(cp.Text, '($2', true);
  688. // replace last gtCommandEnd with ')' and delete everything code behind
  689. j := csi.Items.Count-1;
  690. while (j > i) and not (csi.Items[j].Token = gtCommandEnd) do
  691. dec(j);
  692. if (j > i) then
  693. csi.Items[j] := TCodePart.Create(gtNormal, ')');
  694. inc(j);
  695. while (j < csi.Items.Count) do begin
  696. if (csi.Items[j].Token in [gtNormal, gtLineBreak, gtCommandEnd])
  697. then csi.Items.Delete(j)
  698. else inc(j);
  699. end;
  700. // merge
  701. csi.Merge(aCommand, i+1);
  702. end;
  703. dec(i);
  704. end;
  705. end;
  706. finally
  707. FreeAndNil(rx);
  708. end;
  709. result := self;
  710. end;
  711. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  712. procedure TengShaderGeneratorArgs.GenerateParameterCode(const aTypes: CengShaderPartArr);
  713. var
  714. m: TParameterMap;
  715. p: TengShaderPart;
  716. begin
  717. PushCode;
  718. PushFlags(Flags + [gfGenerateParameterCode] - [gfAddParameterItem]);
  719. m := TParameterMap.Create(false);
  720. try
  721. fMaxParameterLength := 0;
  722. for p in fParameters do begin
  723. if CheckType(p, aTypes) then with (p as TengShaderPartParameter) do begin
  724. fMaxParameterLength := Max(fMaxParameterLength, Length(Typ));
  725. m.Add(Typ+Name, p);
  726. end;
  727. end;
  728. for p in m do begin
  729. (p as TengShaderPartParameter).GenerateCodeIntern(self);
  730. AddLineBreak;
  731. end;
  732. finally
  733. FreeAndNil(m);
  734. PopFlags;
  735. PopCode([pcfPrepend, pcfAddEmptyLine]);
  736. end;
  737. end;
  738. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  739. procedure TengShaderGeneratorArgs.GenerateProcedureCode;
  740. var
  741. i: Integer;
  742. begin
  743. i := 0;
  744. while (i < fProcedures.Count) do begin
  745. PushCode;
  746. PushFlags([gfGenerateProcedureCode, gfAddParameterItem]);
  747. try
  748. (fProcedures[i] as TengShaderPartProc).GenerateCodeIntern(self);
  749. finally
  750. PopFlags;
  751. PopCode([pcfPrepend, pcfAddEmptyLine]);
  752. end;
  753. inc(i);
  754. end;
  755. end;
  756. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  757. procedure TengShaderGeneratorArgs.GenerateMetaCode;
  758. var
  759. layouts: TStringList;
  760. m: TengMetaData;
  761. vCompat: Boolean;
  762. vMax, i: Integer;
  763. s: String;
  764. begin
  765. PushCode;
  766. vMax := 0;
  767. vCompat := false;
  768. layouts := TStringList.Create;
  769. try
  770. for m in fMetaDataList do begin
  771. case m.MetaType of
  772. metaVersion: begin
  773. if (m.Values[0] = VERSION_EXTRA_COMPAT) then
  774. vCompat := true
  775. else if TryStrToInt(m.Values[0], i) then
  776. vMax := max(vMax, i);
  777. if (m.Count > 1) and (m.Values[1] = VERSION_EXTRA_COMPAT) then
  778. vCompat := true;
  779. end;
  780. metaExtension: begin
  781. AddText(format('#extension %s : %s', [m.Values[0], m.Values[1]]));
  782. AddLineBreak;
  783. end;
  784. metaLayout: begin
  785. layouts.Add('layout' + m.Values[0] + ';');
  786. end;
  787. end;
  788. end;
  789. if (vMax >= LAYOUT_MIN_VERSION) then begin
  790. for s in layouts do begin
  791. AddText(s);
  792. AddLineBreak;
  793. end;
  794. end;
  795. if (vMax > 0) then begin
  796. PushCode;
  797. try
  798. AddText('#version ' + IntToStr(vMax));
  799. if vCompat then
  800. AddText(' ' + VERSION_EXTRA_COMPAT);
  801. AddLineBreak;
  802. finally
  803. PopCode([pcfPrepend]);
  804. end;
  805. end;
  806. finally
  807. PopCode([pcfPrepend, pcfAddEmptyLine]);
  808. FreeAndNil(layouts);
  809. end;
  810. end;
  811. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  812. procedure TengShaderGeneratorArgs.GenerateCode(const aCode: TengShaderCode);
  813. begin
  814. fCode.Last.GenerateCode(aCode);
  815. end;
  816. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  817. constructor TengShaderGeneratorArgs.Create(const aRoot: TengShaderPart);
  818. begin
  819. inherited Create;
  820. fCode := TCodeStack.Create(true);
  821. fFlagStack := TengGenerateFlagsStack.Create();
  822. fMetaDataList := TengMetaDataList.Create(false);
  823. fParameters := TParameterMap.Create(false);
  824. fProcedures := TProcedureList.Create(false);
  825. fProcParams := TProcParamStack.Create(false);
  826. fRoot := aRoot;
  827. fInlineReturnCounter := 0;
  828. PushCode;
  829. PushFlags([ gfAddParameterItem, gfAddProcedureItem ]);
  830. end;
  831. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  832. destructor TengShaderGeneratorArgs.Destroy;
  833. begin
  834. FreeAndNil(fProcParams);
  835. FreeAndNil(fProcedures);
  836. FreeAndNil(fParameters);
  837. FreeAndNil(fMetaDataList);
  838. FreeAndNil(fFlagStack);
  839. FreeAndNil(fCode);
  840. inherited Destroy;
  841. end;
  842. end.