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.

927 lines
32 KiB

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