25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

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