Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

823 rindas
28 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. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  125. //TengShaderGeneratorArgs.TCodePart///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  126. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  127. function TengShaderGeneratorArgs.TCodePart.GetCode: String;
  128. begin
  129. case fToken of
  130. gtNormal,
  131. gtCommandEnd:
  132. result := fText;
  133. gtLineBreak:
  134. result := sLineBreak;
  135. else
  136. result := '';
  137. end;
  138. end;
  139. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  140. function TengShaderGeneratorArgs.TCodePart.GetDebugText: String;
  141. begin
  142. case fToken of
  143. gtNormal: result := '[N]' + fText;
  144. gtLineBreak: result := sLineBreak;
  145. gtCommandEnd: result := '[C]' + fText;
  146. gtBlockBegin: result := format('[B%d]', [fIndent]);
  147. gtBlockEnd: result := '[E]';
  148. gtToken: result := '[T' + fText + ']';
  149. gtAppendToPrev: result := '[A]';
  150. else
  151. result := '[' + IntToStr(Integer(fToken)) + ']' + fText
  152. end;
  153. end;
  154. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  155. constructor TengShaderGeneratorArgs.TCodePart.Create(const aToken: TengGeneratorToken; const aText: String; const aIndent: Integer);
  156. begin
  157. inherited Create;
  158. fToken := aToken;
  159. fText := aText;
  160. fIndent := aIndent;
  161. end;
  162. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  163. //TengShaderGeneratorArgs.TCodeStackItem//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  164. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  165. function TengShaderGeneratorArgs.TCodeStackItem.GetIsEmpty: Boolean;
  166. begin
  167. result := (fItems.Count = 0);
  168. end;
  169. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  170. procedure TengShaderGeneratorArgs.TCodeStackItem.GenerateCode(const aCode: TengShaderCode);
  171. type
  172. TGenFlag = (
  173. gfToken, // current line has a token in it
  174. gfTokenOnly, // current line has only a token (or whitespaces) in it
  175. gfPrevIsEmpty, // previouse line was empty (or whitespaces only)
  176. gfCodeIsEmpty, // nothing has added to code
  177. gfAddToPrev // add current line to previouse line
  178. );
  179. TGenFlags = set of TGenFlag;
  180. var
  181. i: Integer;
  182. f: TGenFlags;
  183. line: String;
  184. function GetStrOffset(const aStr: String): Integer;
  185. var
  186. len: Integer;
  187. begin
  188. result := 0;
  189. len := Length(aStr);
  190. while (result < len) and (aStr[result+1] in WHITESPACES) do
  191. inc(result);
  192. end;
  193. function GetOffset(const aCodePart: TCodePart): Integer;
  194. begin
  195. result := GetStrOffset(line);
  196. end;
  197. function GetMinOffset(aStartIndex: Integer): Integer;
  198. begin
  199. if (Trim(line) <> '')
  200. then result := GetStrOffset(line)
  201. else result := High(Integer);
  202. while (aStartIndex < aCode.Count) do begin
  203. if (Trim(aCode[aStartIndex]) <> '') then
  204. result := Min(result, GetStrOffset(aCode[aStartIndex]));
  205. inc(aStartIndex);
  206. end;
  207. if (result >= High(Integer)) then
  208. result := 0;
  209. end;
  210. function TrimLeftLen(const aStr: String; aLen: Integer): String;
  211. var
  212. i, len: Integer;
  213. begin
  214. i := 1;
  215. len := Length(aStr);
  216. while (i <= len) and (aStr[i] in WHITESPACES) and (aLen > 0) do begin
  217. inc(i);
  218. dec(aLen);
  219. end;
  220. result := Copy(aStr, i, len - i + 1);
  221. end;
  222. function PrepareStr(const aStr: String; const aOffset: Integer): String;
  223. begin
  224. if (aOffset > 0)
  225. then result := StringOfChar(' ', aOffset) + aStr
  226. else result := TrimLeftLen(aStr, -aOffset);
  227. end;
  228. procedure IndentBlock(aStartIndex: Integer; const aIndent: Integer);
  229. var
  230. o: Integer;
  231. begin
  232. o := aIndent - GetMinOffset(aStartIndex);
  233. while (aStartIndex < aCode.Count) do begin
  234. aCode[aStartIndex] := PrepareStr(aCode[aStartIndex], o);
  235. inc(aStartIndex);
  236. end;
  237. if (Trim(line) <> '') then
  238. line := PrepareStr(line, o);
  239. end;
  240. procedure ProgressBlock(const aIndent: Integer);
  241. var
  242. s: String;
  243. cp: TCodePart;
  244. FirstLineIndex: Integer;
  245. begin
  246. FirstLineIndex := aCode.Count;
  247. while (i < fItems.Count) do begin
  248. cp := fItems[i];
  249. inc(i);
  250. case cp.Token of
  251. gtLineBreak: begin
  252. if (Trim(line) = '') then begin
  253. if (f * [gfTokenOnly, gfPrevIsEmpty] = []) then begin
  254. aCode.Add(line);
  255. Include(f, gfPrevIsEmpty);
  256. end;
  257. end else begin
  258. if (gfAddToPrev in f) and not (gfCodeIsEmpty in f)
  259. then aCode[aCode.Count-1] := aCode[aCode.Count-1] + TrimLeft(line)
  260. else aCode.Add(line);
  261. Exclude(f, gfPrevIsEmpty);
  262. Exclude(f, gfCodeIsEmpty);
  263. end;
  264. Exclude(f, gfToken);
  265. Exclude(f, gfTokenOnly);
  266. Exclude(f, gfAddToPrev);
  267. line := '';
  268. end;
  269. gtToken: begin
  270. Include(f, gfToken);
  271. if (Trim(line) = '') then
  272. Include(f, gfTokenOnly);
  273. end;
  274. gtBlockBegin: begin
  275. Include(f, gfPrevIsEmpty);
  276. ProgressBlock(GetStrOffset(line) + cp.Indent);
  277. end;
  278. gtBlockEnd: begin
  279. IndentBlock(FirstLineIndex, aIndent);
  280. exit;
  281. end;
  282. gtAppendToPrev: begin
  283. if (Trim(line) = '') and not (gfPrevIsEmpty in f) then
  284. include(f, gfAddToPrev);
  285. end;
  286. else
  287. s := cp.Code;
  288. line := line + s;
  289. end;
  290. end;
  291. end;
  292. {$IFDEF DEBUG}
  293. procedure GenerateDebugCode;
  294. var
  295. cp: TCodePart;
  296. s: String;
  297. sl: TStringList;
  298. begin
  299. s := '';
  300. for cp in fItems do
  301. s := s + cp.DebugText;
  302. sl := TStringList.Create;
  303. try
  304. sl.Text := s;
  305. sl.SaveToFile('C:\Users\Erik\Desktop\tmp\debug.txt');
  306. finally
  307. FreeAndNil(sl);
  308. end;
  309. end;
  310. {$ENDIF}
  311. begin
  312. i := 0;
  313. f := [gfPrevIsEmpty, gfCodeIsEmpty];
  314. line := '';
  315. {$IFDEF DEBUG}
  316. GenerateDebugCode;
  317. {$ENDIF}
  318. ProgressBlock(0);
  319. end;
  320. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  321. procedure TengShaderGeneratorArgs.TCodeStackItem.Merge(const aItem: TCodeStackItem; aIndex: Integer);
  322. begin
  323. if (aIndex < 0) then
  324. aIndex := 0;
  325. if (aIndex > fItems.Count) then
  326. aIndex := fItems.Count;
  327. while (aItem.Items.Count > 0) do
  328. fItems.Insert(aIndex, aItem.Items.PopLast(false));
  329. end;
  330. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  331. constructor TengShaderGeneratorArgs.TCodeStackItem.Create;
  332. begin
  333. inherited Create;
  334. fItems := TCodePartList.Create(true);
  335. end;
  336. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  337. destructor TengShaderGeneratorArgs.TCodeStackItem.Destroy;
  338. begin
  339. FreeAndNil(fItems);
  340. inherited Destroy;
  341. end;
  342. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  343. //TengShaderGeneratorArgs///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  344. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  345. function TengShaderGeneratorArgs.GetFlags: TengGenerateFlags;
  346. begin
  347. result := fFlagStack.Last;
  348. end;
  349. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  350. function TengShaderGeneratorArgs.GetProcParams: TStrings;
  351. begin
  352. result := fProcParams.Last;
  353. end;
  354. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  355. procedure TengShaderGeneratorArgs.GenerateParameterCode(const aTypes: CengShaderPartArr);
  356. var
  357. m: TParameterMap;
  358. p: TengShaderPart;
  359. begin
  360. PushCode;
  361. PushFlags(Flags + [gfGenerateParameterCode] - [gfAddParameterItem]);
  362. m := TParameterMap.Create(false);
  363. try
  364. fMaxParameterLength := 0;
  365. for p in fParameters do begin
  366. if CheckType(p, aTypes) then with (p as TengShaderPartParameter) do begin
  367. fMaxParameterLength := Max(fMaxParameterLength, Length(Typ));
  368. m.Add(Typ+Name, p);
  369. end;
  370. end;
  371. for p in m do begin
  372. (p as TengShaderPartParameter).GenerateCodeIntern(self);
  373. AddLineBreak;
  374. end;
  375. finally
  376. FreeAndNil(m);
  377. PopFlags;
  378. PopCode([pcfPrepend, pcfAddEmptyLine]);
  379. end;
  380. end;
  381. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  382. procedure TengShaderGeneratorArgs.GenerateProcedureCode;
  383. var
  384. i: Integer;
  385. begin
  386. i := 0;
  387. while (i < fProcedures.Count) do begin
  388. PushCode;
  389. PushFlags([gfGenerateProcedureCode, gfAddParameterItem]);
  390. try
  391. (fProcedures[i] as TengShaderPartProc).GenerateCodeIntern(self);
  392. finally
  393. PopFlags;
  394. PopCode([pcfPrepend, pcfAddEmptyLine]);
  395. end;
  396. inc(i);
  397. end;
  398. end;
  399. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  400. procedure TengShaderGeneratorArgs.GenerateMetaCode;
  401. var
  402. layouts: TStringList;
  403. m: TengMetaData;
  404. vCompat: Boolean;
  405. vMax, i: Integer;
  406. s: String;
  407. begin
  408. PushCode;
  409. vMax := 0;
  410. vCompat := false;
  411. layouts := TStringList.Create;
  412. try
  413. for m in fMetaDataList do begin
  414. case m.MetaType of
  415. metaVersion: begin
  416. if (m.Values[0] = VERSION_EXTRA_COMPAT) then
  417. vCompat := true
  418. else if TryStrToInt(m.Values[0], i) then
  419. vMax := max(vMax, i);
  420. if (m.Count > 1) and (m.Values[1] = VERSION_EXTRA_COMPAT) then
  421. vCompat := true;
  422. end;
  423. metaExtension: begin
  424. AddText(format('#extension %s : %s', [m.Values[0], m.Values[1]]));
  425. AddLineBreak;
  426. end;
  427. metaLayout: begin
  428. layouts.Add('layout' + m.Values[0] + ';');
  429. end;
  430. end;
  431. end;
  432. if (vMax >= LAYOUT_MIN_VERSION) then begin
  433. for s in layouts do begin
  434. AddText(s);
  435. AddLineBreak;
  436. end;
  437. end;
  438. if (vMax > 0) then begin
  439. PushCode;
  440. try
  441. AddText('#version ' + IntToStr(vMax));
  442. if vCompat then
  443. AddText(' ' + VERSION_EXTRA_COMPAT);
  444. AddLineBreak;
  445. finally
  446. PopCode([pcfPrepend]);
  447. end;
  448. end;
  449. finally
  450. PopCode([pcfPrepend, pcfAddEmptyLine]);
  451. FreeAndNil(layouts);
  452. end;
  453. end;
  454. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  455. procedure TengShaderGeneratorArgs.PushCode;
  456. begin
  457. fCode.PushLast(TCodeStackItem.Create);
  458. end;
  459. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  460. procedure TengShaderGeneratorArgs.PushFlags(const aFlags: TengGenerateFlags);
  461. begin
  462. fFlagStack.PushLast(aFlags);
  463. end;
  464. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  465. procedure TengShaderGeneratorArgs.PushProcParams(const aParams: TStrings);
  466. begin
  467. fProcParams.PushLast(aParams);
  468. end;
  469. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  470. procedure TengShaderGeneratorArgs.PopCode(const aFlags: TengPopCodeFlags);
  471. var
  472. csi: TCodeStackItem;
  473. begin
  474. csi := fCode.PopLast(false);
  475. try
  476. if csi.IsEmpty then
  477. exit;
  478. if (pcfPrepend in aFlags) then begin
  479. if (pcfAddEmptyLine in aFlags) then
  480. csi.Items.Add(TCodePart.Create(gtLineBreak, ''));
  481. fCode.Last.Merge(csi, 1);
  482. end else if (pcfAppend in aFlags) then begin
  483. if (pcfAddEmptyLine in aFlags) then
  484. fCode.Last.Items.Add(TCodePart.Create(gtLineBreak, ''));
  485. fCode.Last.Merge(csi, fCode.Last.Items.Count);
  486. end;
  487. finally
  488. FreeAndNil(csi);
  489. end;
  490. end;
  491. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  492. procedure TengShaderGeneratorArgs.PopFlags;
  493. begin
  494. fFlagStack.PopLast(true);
  495. end;
  496. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  497. procedure TengShaderGeneratorArgs.PopProcParams;
  498. begin
  499. fProcParams.PopLast;
  500. end;
  501. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  502. function TengShaderGeneratorArgs.AddText(const aText: String): TengShaderGeneratorArgs;
  503. begin
  504. fCode.Last.Items.Add(TCodePart.Create(gtNormal, aText));
  505. result := self;
  506. end;
  507. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  508. function TengShaderGeneratorArgs.AddToken(const aToken: String): TengShaderGeneratorArgs;
  509. begin
  510. fCode.Last.Items.Add(TCodePart.Create(gtToken, aToken));
  511. result := self;
  512. end;
  513. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  514. function TengShaderGeneratorArgs.AddCommandEnd(const aToken: String): TengShaderGeneratorArgs;
  515. begin
  516. fCode.Last.Items.Add(TCodePart.Create(gtCommandEnd, aToken));
  517. result := self;
  518. end;
  519. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  520. function TengShaderGeneratorArgs.AddLineBreak: TengShaderGeneratorArgs;
  521. begin
  522. fCode.Last.Items.Add(TCodePart.Create(gtLineBreak, ''));
  523. result := self;
  524. end;
  525. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  526. function TengShaderGeneratorArgs.BeginBlock(const aIndent: Integer): TengShaderGeneratorArgs;
  527. begin
  528. fCode.Last.Items.Add(TCodePart.Create(gtBlockBegin, '', aIndent));
  529. result := self;
  530. end;
  531. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  532. function TengShaderGeneratorArgs.EndBlock(const aCanAppend: Boolean): TengShaderGeneratorArgs;
  533. begin
  534. fCode.Last.Items.Add(TCodePart.Create(gtBlockEnd, ''));
  535. result := self;
  536. end;
  537. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  538. function TengShaderGeneratorArgs.AppendToPrevLine: TengShaderGeneratorArgs;
  539. begin
  540. fCode.Last.Items.Add(TCodePart.Create(gtAppendToPrev, ''));
  541. result := self;
  542. end;
  543. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  544. procedure TengShaderGeneratorArgs.AddMeta(const aMeta: TengMetaData);
  545. begin
  546. fMetaDataList.Add(aMeta);
  547. end;
  548. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  549. procedure TengShaderGeneratorArgs.AddParameter(const aParam: TengShaderPart);
  550. var
  551. p: TengShaderPart;
  552. s: String;
  553. begin
  554. if not (aParam is TengShaderPartParameter) then
  555. raise EengShaderPartInternal.Create('parameter (' + aParam.ClassName + ') is not a ' + TengShaderPartParameter.ClassName, aParam);
  556. with (aParam as TengShaderPartParameter) do begin
  557. p := fParameters[Name];
  558. if Assigned(p) then begin
  559. s := Format('use of duplicate identifier: %s (%s %d:%d)', [Name, Filename, Line + 1, Col]) + sLineBreak +
  560. 'previously declared here:' + sLineBreak +
  561. Format(' %s %d:%d', [p.Filename, p.Line + 1, p.Col]) + sLineBreak;
  562. fRoot.LogMsg(llWarning, s);
  563. fParameters[Name] := aParam;
  564. end else
  565. fParameters.Add(Name, aParam);
  566. end;
  567. end;
  568. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  569. procedure TengShaderGeneratorArgs.AddProcedure(const aProc: TengShaderPart);
  570. begin
  571. if not (aProc is TengShaderPartProc) then
  572. raise EengShaderPartInternal.Create('parameter (' + aProc.ClassName + ') is not a ' + TengShaderPartProc.ClassName, aProc);
  573. fProcedures.Add(aProc);
  574. end;
  575. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  576. function TengShaderGeneratorArgs.ExtractCurrentCommand(const aCommand: TCodeStackItem): Integer;
  577. var
  578. csi: TCodeStackItem;
  579. i, len: Integer;
  580. s: String;
  581. begin
  582. csi := fCode.Last;
  583. if not Assigned(aCommand) then
  584. exit;
  585. // find last command end token
  586. while (csi.Items.Last.Token <> gtCommandEnd) do
  587. aCommand.Items.PushFirst(csi.Items.PopLast(false));
  588. // move forward to first code part with text
  589. while (aCommand.Items.First.Token <> gtNormal) or (Trim(aCommand.Items.First.Text) = '') do
  590. csi.Items.PushLast(aCommand.Items.PopFirst(false));
  591. // extract leading whitespaces
  592. i := 1;
  593. s := aCommand.Items.First.Text;
  594. len := Length(s);
  595. while (s[i] in WHITESPACES) and (i <= len) do
  596. inc(i);
  597. csi.Items.PushLast(TCodePart.Create(gtNormal, Copy(s, 1, i-1)));
  598. aCommand.Items.First.Text := copy(s, i, len-i+1);
  599. result := i - 1;
  600. end;
  601. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  602. procedure TengShaderGeneratorArgs.ReplaceIdents(const aOld, aNew: TStrings);
  603. var
  604. rx: TRegExpr;
  605. i: Integer;
  606. cp: TCodePart;
  607. begin
  608. if (aOld.Count <> aNew.Count) then
  609. raise EengShaderPartInternal.Create('old and new ident must have the same size');
  610. rx := TRegExpr.Create;
  611. try
  612. for i := 0 to aOld.Count-1 do begin
  613. rx.Expression := '([^A-z0-9_]+|^)' + aOld[i] + '([^A-z0-9_]+|$)';
  614. for cp in fCode.Last.Items do
  615. cp.Text := rx.Replace(cp.Text, '$1' + aNew[i] + '$2', true);
  616. end;
  617. finally
  618. FreeAndNil(rx);
  619. end;
  620. end;
  621. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  622. procedure TengShaderGeneratorArgs.ReplaceReturns(const aCommand: TCodeStackItem; const aRetType, aName: String);
  623. var
  624. rx: TRegExpr;
  625. RetCount, i, j: Integer;
  626. csi: TCodeStackItem;
  627. cp: TCodePart;
  628. s: String;
  629. begin
  630. rx := TRegExpr.Create;
  631. try
  632. rx.Expression := '([^A-z0-9_]+|^)return([^A-z0-9_]+|$)';
  633. csi := fCode.Last;
  634. // find number of "return" in code
  635. RetCount := 0;
  636. for cp in csi.Items do begin
  637. s := cp.Code;
  638. if rx.Exec(s) then
  639. inc(RetCount);
  640. end;
  641. // no "return" found
  642. if (RetCount = 0) then begin
  643. raise EengShaderPartInternal.Create('expected "return" token in function');
  644. // more than one "return"
  645. end else if (RetCount > 1) then begin
  646. // find block begin
  647. i := 0;
  648. while (csi.Items[i].Token <> gtBlockBegin) and (i < csi.Items.Count) do
  649. inc(i);
  650. if (i < csi.Items.Count)
  651. then inc(i)
  652. else i := 0;
  653. // insert temp variable
  654. s := Format('%s_ret%.4d', [aName, fInlineReturnCounter]);
  655. inc(fInlineReturnCounter);
  656. csi.Items.Insert(i, TCodePart.Create(gtLineBreak, ''));
  657. csi.Items.Insert(i+1, TCodePart.Create(gtNormal, aRetType + ' ' + s));
  658. csi.Items.Insert(i+2, TCodePart.Create(gtCommandEnd, ';'));
  659. csi.Items.Insert(i+3, TCodePart.Create(gtLineBreak, ''));
  660. // replace "return" with temp variable
  661. for cp in csi.Items do
  662. cp.Text := rx.Replace(cp.Text, '$1' + s + ' =$2', true);
  663. // merge code
  664. csi.Merge(aCommand, csi.Items.Count);
  665. AddText(s);
  666. // exact one "return"
  667. end else begin
  668. i := csi.Items.Count-1;
  669. while (i > 0) do begin
  670. cp := csi.Items[i];
  671. if rx.Exec(cp.Text) then begin
  672. csi.Items.Insert(i, TCodePart.Create(gtNormal, rx.Match[1]));
  673. cp.Text := rx.Replace(cp.Text, '(', true);
  674. // replace last gtCommandEnd with ')' and delete everything behind
  675. j := csi.Items.Count-1;
  676. while (j > i) and not (csi.Items[j].Token = gtCommandEnd) do
  677. dec(j);
  678. if (j > i) then
  679. csi.Items[j] := TCodePart.Create(gtNormal, ')');
  680. while (csi.Items.Count-1 > j) do
  681. csi.Items.PopLast(true);
  682. // merge
  683. csi.Merge(aCommand, i+1);
  684. end;
  685. dec(i);
  686. end;
  687. end;
  688. finally
  689. FreeAndNil(rx);
  690. end;
  691. end;
  692. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  693. procedure TengShaderGeneratorArgs.GenerateCode(const aCode: TengShaderCode);
  694. begin
  695. GenerateProcedureCode;
  696. GenerateParameterCode(CengShaderPartArr.Create(TengShaderPartVar));
  697. GenerateParameterCode(CengShaderPartArr.Create(TengShaderPartVarying));
  698. GenerateParameterCode(CengShaderPartArr.Create(TengShaderPartUniform));
  699. GenerateMetaCode;
  700. fCode.Last.GenerateCode(aCode);
  701. end;
  702. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  703. constructor TengShaderGeneratorArgs.Create(const aRoot: TengShaderPart);
  704. begin
  705. inherited Create;
  706. fCode := TCodeStack.Create(true);
  707. fFlagStack := TengGenerateFlagsStack.Create();
  708. fMetaDataList := TengMetaDataList.Create(false);
  709. fParameters := TParameterMap.Create(false);
  710. fProcedures := TProcedureList.Create(false);
  711. fProcParams := TProcParamStack.Create(false);
  712. fRoot := aRoot;
  713. fInlineReturnCounter := 0;
  714. PushCode;
  715. PushFlags([ gfAddParameterItem, gfAddProcedureItem ]);
  716. end;
  717. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  718. destructor TengShaderGeneratorArgs.Destroy;
  719. begin
  720. FreeAndNil(fProcParams);
  721. FreeAndNil(fProcedures);
  722. FreeAndNil(fParameters);
  723. FreeAndNil(fMetaDataList);
  724. FreeAndNil(fFlagStack);
  725. FreeAndNil(fCode);
  726. inherited Destroy;
  727. end;
  728. end.