Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Mär 28, 2024 18:33

Foren-Übersicht » Programmierung » Einsteiger-Fragen
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 10 Beiträge ] 
Autor Nachricht
BeitragVerfasst: Mi Mai 30, 2018 09:53 
Offline
DGL Member

Registriert: Mo Jul 17, 2006 13:16
Beiträge: 69
Hallo!
Mit meinem Hauptprogramm (Delphi 2010 + dglOpenGL) lasse ich in einem Fenster etwas mit OpenGL darstellen; mehrere GL-Objekte können enthalten sein. Über das Hauptprogramm kann man Unterprogramme öffnen, um die Objekte zu modifizieren (Änderungen werden erst angewendet, wenn das Formular geschlossen wird). Konkret: Ich lasse Text anzeigen (LossyX-TextSuite, danke!), mit einem Dialog kann ich die Schrift ändern.

In diesem Dialog wird -in einem neuen GL-Fenster- ein einziger Beispieltext angezeigt, und die ausgewählten Optionen (Schriftart, -farbe, ...) werden im nächsten Renderdurchlauf direkt sichtbar - nur in diesem Dialog, nicht im Hauptprogramm (wie beabsichtigt). Funktioniert für sich genommen auch alles einwandfrei. Lediglich wenn ich beide Projekte zusammenfüge und somit zwei OpenGL-Fenster in einer Anwendung habe gibt es ein Problem:

Sobald ich den Dialog beende (die gewählten Einstellungen werden gespeichert und an alle glText-Objekte übergeben, der Dialog wird freigegeben) gibt es im nächsten Renderdurchlauf des Hauptprogramms nur noch Exceptions.

Ich gehe davon aus, dass das zweite Formular etwas freigibt (DC, RC,...?) was eigentlich vom ersten Formular (Hauptprogramm) noch verwendet wird. Unten seht ihr das Formular, das sowohl im Hauptprogramm als auch in dem Dialog eingebettet wird. Imo wird von/für jedes Formular ja ein eigener DC/RC erzeugt(?), also sollte der jeweils auch wieder Problemlos freigegeben werden können?

Die eigentlichen Objekte des Hauptprogramms sind unabhängig vom zweiten Fenster, also sollte von denen keines versehen freigegeben worden sein o.ä....

Vielleicht habt Ihr eine Idee? Danke im voraus!

Code:
  1. unit HAL1_GRAPHICS_glFormTemplate;
  2.  
  3. interface
  4.  
  5. {$REGION 'uses 1.1'}
  6. uses
  7.   ADODB, Classes, Contnrs, Controls, DateUtils, Dialogs, DB, ComCtrls, ExtCtrls,
  8.   Forms, Graphics, Menus, Messages, StdCtrls, SysUtils,
  9.   Types, Variants, Windows
  10.   , dglOpenGL
  11.   , ...
  12.   ;
  13. {$ENDREGION}
  14.  
  15. type
  16.   TglForm_Template = class(TForm)
  17.   private
  18.     FDC: HDC;
  19.     FRC: HGLRC;
  20.     FParentComponent: TWinControl;
  21.     FblActive: Boolean;
  22.     Raster: TGLObjectTile;
  23.     clglObjects: TComponentList;
  24.     timRenderTimer: TTimer;
  25.     procedure DoRenderTimer(Sender: TObject);
  26.     procedure SetActive(_Active: Boolean);
  27.   protected
  28.     procedure RenderEmptyBackground;
  29.   public
  30.     Debug: Boolean;
  31.     Background_RenderRaster: Boolean;
  32.     Background_Color: TColor;
  33.     TextSuiteManager: TglTextSuiteManager;
  34.     procedure Render;
  35.  
  36.     property DC: HDC read FDC;
  37.     property RC: HGLRC read FRC;
  38.     property Active: Boolean read FblActive write SetActive;
  39.     property Parent: TWinControl read FParentComponent write SetParent;
  40.  
  41.     function AddglObject(_glObject: TGLObject): Boolean;
  42.     function RemoveglObject(_glObject: TGLObject): Boolean;
  43.     procedure SetMyParentComponent(_Parent: TWinControl);
  44.  
  45.     constructor Create(_Owner: TComponent); overload; override;
  46.     destructor Destroy; override;
  47.   end;
  48.  
  49. implementation
  50. {$I 'compileoptions.inc'}
  51. {$WARN UNIT_PLATFORM OFF}
  52. {$WARN SYMBOL_PLATFORM OFF}
  53.  
  54.  
  55. { TglForm_Template }
  56.  
  57. function TglForm_Template.AddglObject(_glObject: TGLObject): Boolean;
  58. begin
  59.   Result := False;
  60.  
  61.   if not assigned(_glObject) then Exit;
  62.   if not _glObject.InheritsFrom(TGLObject) then Exit;
  63.  
  64.   if clglObjects.IndexOf(_glObject) < 0 then begin
  65.     TGLObject(_glObject).DC := Self.DC;
  66.     TGLObject(_glObject).RC := Self.RC;
  67.     TGLObject(_glObject).TextSuiteManager := Self.TextSuiteManager;
  68.  
  69.     clglObjects.Add(_glObject);
  70.   end;
  71.  
  72.   Result := (clglObjects.IndexOf(_glObject) > -1);
  73. end;
  74.  
  75. constructor TglForm_Template.Create(_Owner: TComponent);
  76. begin
  77.   try
  78.     inherited CreateNew(_Owner);
  79.     Debug := False;
  80.  
  81.     {$Region 'OpenGL initialisieren'}
  82.     try
  83.       FblActive := False;
  84.       FParentComponent := NIL;
  85.  
  86.       clglObjects := TComponentList.Create(False);
  87.  
  88.       FDC := GetDC(Handle);
  89.       if not InitOpenGL then begin
  90.         MessageBox(_('OpenGL konnte nicht initialisiert werden. Anwendung wird beendet!'), MB_ICONERROR + MB_OK);
  91.         Application.Terminate;
  92.       end;
  93.       FRC:= CreateRenderingContext( DC,
  94.                                    [opDoubleBuffered],
  95.                                    32,
  96.                                    24,
  97.                                    0,0,0,
  98.                                    0);
  99.       ActivateRenderingContext(DC, RC);
  100.  
  101.       ReadExtensions;
  102.       ReadImplementationProperties;
  103.  
  104.       Visible := True;
  105.       OnResize := Self.DoRenderTimer;
  106.     except
  107.       on E: SysUtils.Exception do begin
  108.         Log('TglForm_Template.Create.2', M, E.Message, ws_SEVERITY_EXCEPTION);
  109.       end;
  110.     end;
  111.     {$EndRegion}
  112.  
  113.     // Hintergrundraster erstellen
  114.     Raster := TGLObjectTile.Create(Self);
  115.     with Raster do begin
  116.       Raster.DC := Self.DC;
  117.       Left := 0;
  118.       Top := 0;
  119.     end;
  120.  
  121.     timRenderTimer := TTimer.Create(Self);
  122.     with timRenderTimer do begin
  123.       Enabled := False;
  124.       Interval := 1000 div 120;
  125.       OnTimer := DoRenderTimer;
  126.     end;
  127.  
  128.     Active := True;
  129.     Background_RenderRaster := True;
  130.     Background_Color := $00660000;
  131.  
  132.     TextSuiteManager := TglTextSuiteManager.Create(Self);
  133.   except
  134.     on E: SysUtils.Exception do begin
  135.       Log('TglForm_Template.Create.1', M, E.Message, ws_SEVERITY_EXCEPTION);
  136.     end;
  137.   end;
  138. end;
  139.  
  140. destructor TglForm_Template.Destroy;
  141. begin
  142.   Active := False;
  143.   FreeAndNil(TextSuiteManager);
  144.   FreeAndNil(clglObjects);
  145.  
  146.   FreeAndNil(Raster);
  147.  
  148.   try
  149.     try
  150.       // Renderkontext deaktiveren
  151.       DeactivateRenderingContext;
  152.       // Renderkontext "befreien"
  153.       wglDeleteContext(RC);
  154.       // Erhaltenen Gerätekontext auch wieder freigeben
  155.       ReleaseDC(Handle, DC);
  156.     except
  157.  
  158.     end;
  159.   finally
  160.     inherited;
  161.   end;
  162. end;
  163.  
  164. function TglForm_Template.RemoveglObject(_glObject: TGLObject): Boolean;
  165. begin
  166.   Result := False;
  167.   try
  168.     _glObject.TextSuiteManager := NIL;
  169.     clglObjects.Remove(_glObject);
  170.  
  171.     Result := (clglObjects.IndexOf(_glObject) < 0);
  172.   except end;
  173. end;
  174.  
  175. procedure TglForm_Template.Render;
  176. var
  177.   i: integer;
  178.   glObject: TGLObject;
  179. begin
  180.   RenderEmptyBackground;
  181.  
  182.   if Background_RenderRaster then begin
  183.     Raster.PositionUserDesigned.Width := Self.ClientWidth;
  184.     Raster.PositionUserDesigned.Height := Self.ClientHeight;
  185.     Raster.Render;
  186.   end;
  187.  
  188.   for i := 0 to clglObjects.Count - 1 do begin
  189.     glObject := TGLObject(clglObjects.Items[i]);
  190.  
  191.     if glObject.DC <> Self.DC then begin
  192.       glObject.DC := Self.DC;
  193.     end;
  194.  
  195.     if glObject.RC <> Self.RC then begin
  196.       glObject.RC := Self.RC;
  197.     end;
  198.  
  199.     glObject.Render;
  200.   end;
  201.  
  202.   SwapBuffers(DC);
  203. end;
  204.  
  205. procedure TglForm_Template.RenderEmptyBackground;
  206. var
  207.   r, g, b: Byte;
  208. begin
  209.   ActivateRenderingContext(DC, RC);
  210.  
  211.   HAL_Color2RGB(Background_Color, r, g, b);
  212.   glClearColor(r/255, g/255, b/255, 1);
  213.   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  214.  
  215.  
  216.   glViewport(0, 0, ClientWidth, ClientHeight);
  217.   glMatrixMode(GL_PROJECTION);
  218.   glLoadIdentity();
  219.  
  220.   glOrtho(0, ClientWidth, ClientHeight, 0, 0, 1000);
  221.   glMatrixMode(GL_MODELVIEW);
  222.   glLoadIdentity;
  223.  
  224.   glEnable(GL_ALPHA_TEST);
  225.   glEnable(GL_CULL_FACE);
  226.   glCullFace(GL_BACK);
  227. end;
  228.  
  229. procedure TglForm_Template.SetActive(_Active: Boolean);
  230. begin
  231.   FblActive := _Active;
  232.   timRenderTimer.Enabled := _Active;
  233. end;
  234.  
  235. procedure TglForm_Template.SetMyParentComponent(_Parent: TWinControl);
  236. begin
  237.   try
  238.     FParentComponent := _Parent;
  239.  
  240.     if assigned(_Parent) then begin
  241.       Windows.SetParent(Self.Handle, _Parent.Handle);
  242.       BorderStyle := bsNone;
  243.       BoundsRect := Rect(0, 0, _Parent.ClientWidth, _Parent.ClientHeight);
  244.     end else begin
  245.       Windows.SetParent(Self.Handle, 0);
  246.       BorderStyle := bsSizeable;
  247.     end;
  248.   except
  249.     on E: SysUtils.Exception do begin
  250.       Log('TglForm_Template.SetMyParentComponent', M, E.Message, ws_SEVERITY_EXCEPTION);
  251.     end;
  252.   end;
  253. end;
  254.  
  255. procedure TglForm_Template.DoRenderTimer(Sender: TObject);
  256. begin
  257.   timRenderTimer.Enabled := False;
  258.   Render;
  259.   timRenderTimer.Enabled := True;
  260. end;
  261.  
  262.  
  263. end.



Edit1:
Das Problem scheint wohl in Verbindung mit der TextSuite zu bestehen:
Code:
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. constructor TtsRendererOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  3. begin
  4.   inherited Create(aContext, aFormat);
  5.   fIsRendering  := false;
  6.   glGenBuffers(1, @fVBO);
  7.   glBindBuffer(GL_ARRAY_BUFFER, fVBO);
  8.   glBufferData(GL_ARRAY_BUFFER, SizeOf(TVertex) * Length(VBO_DATA), @VBO_DATA[0].pos[0], GL_STATIC_DRAW);
  9.   glBindBuffer(GL_ARRAY_BUFFER, 0);
  10. end;
  11.  
  12. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  13. destructor TtsRendererOpenGL.Destroy;
  14. begin
  15.   glDeleteBuffers(1, @fVBO);
  16.   inherited Destroy;
  17. end;


Kann es sein, dass wenn der TtsRendererOpenGL des einen Formulars freigegeben wird, er ALLE *wasauchimmer* löscht? Wenn ich glDeleteBuffers(1, @fVBO) auskommentiere, scheint das Programm (in einer verkleinerten Teststellung) zumindest nicht direkt abzustürzen. Es scheint, als würden auch meine Texturen im Hintergrund freigegeben, diese werden nun zumindest nicht mehr gezeichnet.

Anmerkung: Ich arbeite mit jeweils einer Instanz von der TextSuite pro OpenGLFormular, wahrscheinlich, weil bei gleichzeitiger benutzung der selben Textsuite als globales Objekt es auch Probleme gab (Evtl. durch fehlendes setzen der ActiveRenderContext/wglMakeCurrent pro Objekt / einmal setzen pro Zeichenvorgang lange wohl nicht).

Aktueller Versuch:
Ich versuche nur einen TtsRendererOpenGL zu nutzen, diesen aber global verfügbar zu haben und erst mit Programmende freizugeben.
Ergebnis: Das Problem besteht nach wie vor. Der Renderer erzeut nun auch teilweise Probleme sobald das zweite Formular überhaupt erst erstellt wird.


Code:
  1. procedure TtsRendererOpenGL.Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer);
  2. var
  3.   ref: TtsOpenGLRenderRef;
  4.   m: TtsMatrix4f;
  5. begin
  6.   if Assigned(aRenderRef) then begin
  7.     ref := TtsOpenGLRenderRef(aRenderRef);
  8.  
  9.     glEnable(GL_TEXTURE_2D);
  10.     glBindTexture(GL_TEXTURE_2D, ref.TextureID);
  11.  
  12.     glMatrixMode(GL_TEXTURE);
  13.     glPushMatrix;
  14.     glLoadIdentity;
  15.     glMultMatrixf(@ref.TexMat[0, 0]);
  16.  
  17.     glMatrixMode(GL_MODELVIEW);
  18.     glPushMatrix;
  19.     if (aForcedWidth > 0) then begin
  20.       m := ref.VertMat;
  21.       m[0] := tsVector4f(aForcedWidth, 0, 0, 0);
  22.       glMultMatrixf(@m[0, 0]);
  23.     end else
  24.       glMultMatrixf(@ref.VertMat[0, 0]);
  25.  
  26.       try
  27.     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // hier kommt die Null-Pointer Exception!
  28.       except
  29.         on E: Exception do begin
  30.           sleep(0);
  31.         end;
  32.       end;
  33.  
  34.     glMatrixMode(GL_TEXTURE);
  35.     glPopMatrix;
  36.     glMatrixMode(GL_MODELVIEW);
  37.     glPopMatrix;
  38.   end;
  39. end;



Falls Ihr Tips habt, immer her damit!


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 30, 2018 16:23 
Offline
DGL Member
Benutzeravatar

Registriert: Fr Mai 31, 2002 19:41
Beiträge: 1276
Wohnort: Bäretswil (Schweiz)
Programmiersprache: Pascal
2 OpenGL-Fenster sind eine heikle Sache.

Evtl. kannst du dein Problem umgehen, in dem du 2 Zeichenbereiche mit glViewPort(...) in deinem Form erstellst-

Code:
  1. procedure TForm1.ogcDrawScene(Sender: TObject);
  2. begin
  3.   glViewport(0,0, ClientWidth div 2, ClientHeight);
  4.  
  5.   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  // Frame und Tiefen-Buffer löschen.
  6.  
  7.   Zeichne_deine_Scene_1();
  8.  
  9.   glViewport(ClientWidth div 2,0, ClientWidth div 2, ClientHeight);
  10.  
  11.   Zeichne_deine_Scene_2();
  12.  
  13.   ogc.SwapBuffers;
  14. end;

_________________
OpenGL


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 30, 2018 17:39 
Offline
DGL Member

Registriert: Mo Jul 17, 2006 13:16
Beiträge: 69
Die Idee ist generell nicht verkehrt, aber leider für mich nicht anwendbar.

Der Dialog, in dem ich das Aussehen der Schriftart definiere (Farbe, ...) soll -schon alleine, dass ich ihn in anderen Hauptprogrammen ebenfalls verwenden kann- unabhängig von dem Hauptprogramm sein. Das Hauptprogramm übergibt dem Dialog als Parameter einen String (Einstellungen für die Schriftart), der Dialog gibt nach ShowModal einen String zurück. Einfach und unkompliziert, nichts ist verzahnt, alles störungsfrei - in der Theorie.

Es muss doch technisch möglich sein, einen zweiten, unabhängigen gl-Bereich zu haben, ohne dass ich eine neue .exe machen muss...

Ich habe eben einen noch vereinfachteren Test gemacht mit nur 2x dem TglForm_Template (es verwendet eine TileTextur für ein Hintergrundraster). Hier habe ich auch das Problem, dass beim Freigeben der zweiten Form die Textur der ersten Form ungültig wird, also das Raster nicht mehr dargestellt werden kann.

Ich verstehe immer noch nicht das eigentliche Problem, warum diese beiden gl-Bereiche so miteinander verzahnt sind. Ich könnte mir wohl bei MultiThreading erklären, dass ein glText oder sonstiges Objekt versehentlich auf den falschen glBereich zeichnet, wenn mitten im Zeichnen der andere Thread mit wglMakeCurrent das andere Fenster zum zeichnen aktiviert. Unter dem Vorbehalt, dass mein glForm mit der .Render Prozedur alle Objekte zeichnet bevor das andere Form (im Mainthread, kein Multithreading) den RenderContext neu festlegt, sollte das aber eigentlich kein Problem darstellen?

Ich bin mir immer noch nicht über die Bedeutung oder Notwendigkeit von DC und RC im klaren. Ja, die Wiki kenne ich, aber ich kann das nicht menschenlogisch, praktisch verstehen.

RC könnte ich mir so erklären, dass ich in einem Bildbearbeitungsprogramm mehrere Dateien gleichzeitig offen habe. Nun zeichne ich ein rotes Rechteck. Sofern ich nicht den RC ändere, zeichne ich auf das Bild/die Datei, das gerade das aktive Fenster ist (MDI-Anwendung, oder das "aktive Tab"). Will ich auf ein anderes Bild zeichnen, muss ich durch festlegen des RC erst die Datei/das Fenster im Bildbearbeitungsprogramm auswählen, auf das ich zeichnen möchte. Somit kann ich -weiterhin bildlich gesprochen- auf der zweiten offenen Datei (RC) z.B. ein Fernsehbild in groß zeichnen, und dieses dann als Textur in die erste Datei (RC) verkleinert und perspektivisch verzerrt darstellen.

DC ist für mich das komplizierte Pendant zu .Parent. Klar, so simpel kann man es eigentlich nicht bezeichnen, dennoch gibt es allen gl-Prozeduren und Funktionen vor (oder der Graphikkarte etc.) wo auf den Bildschirm hingezeichnet werden darf. Damit ist natürlich kein Rechteckt gemeint, sondern sowas wie der Hinweis oder "Pointer": "Das Form wird sowieso gezeichnet, wenn du auch was zu zeichnen hast, dann hänge dich doch einfach da dran". Würde ich statt meinem Form den DC vom Desktop oder einem Computerspiel nehmen, könnte ich dort direkt reinzeichnen (ein On-Screen-Display, OSD, wie man es beim Fernsehen beim verändern der Lautstärke sieht).

Nun wissen alle meine Prozeduren also wohin sie zeichnen sollen, vorausgesetzt der DC und RC ist wirklich aktiv. Frage ist halt, warum kommen sie durcheinander? Warum stört es das eine Objekt, wenn das andere nicht existiert?

Bekommen beide glForms evtl. den selben DC, und beim Freigeben des einen stirbt somit auch der Andere?
Sind die Texturen alle in einem Speicherbereich, der mit einem Flush-Befehl oder so _komplett_ geleert wird?

Irgendwie muss das doch gehen.
Kann/soll/muss ich einen anderen DC erstellen? Soll ich mein erstes glForm freigeben bevor ich das Andere erzeuge, und danach neu erstellen?


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 30, 2018 21:19 
Offline
DGL Member
Benutzeravatar

Registriert: Fr Mai 31, 2002 19:41
Beiträge: 1276
Wohnort: Bäretswil (Schweiz)
Programmiersprache: Pascal
Wen du das Compatible-Profil verwendest, habe ich eine Lösung, ist aber für Lazarus.
Mit OpenGL 3.3 habe ich es nicht hingekriegt.

Siehe Muster im Anhang.


Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

_________________
OpenGL


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 30, 2018 21:54 
Offline
DGL Member

Registriert: Mo Jul 17, 2006 13:16
Beiträge: 69
Woher weiß ich, ob ich das Compatible-Profil verwende?

Habe leider kein Lazarus und kann deshalb Deine Demo nicht laufen lassen, aber von den Quelltexten der Units her sieht das doch recht ähnlich aus wie bei mir.

Die Frage ist, wie die Lazarus "TOpenGLControl" anders vom initialisieren / freigeben gehandhabt wird, also ich es mit dem TForm mache, auf dem ich OpenGL initialisiere...


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Mai 30, 2018 22:24 
Offline
DGL Member
Benutzeravatar

Registriert: Fr Mai 31, 2002 19:41
Beiträge: 1276
Wohnort: Bäretswil (Schweiz)
Programmiersprache: Pascal
Zitat:
Woher weiß ich, ob ich das Compatible-Profil verwende?
So lange du Befehle wie glMatrixMode(, etc. verwendest bis du im Compatible-Profil.

Zitat:
Habe leider kein Lazarus und kann deshalb Deine Demo nicht laufen lassen, aber von den Quelltexten der Units her sieht das doch recht ähnlich aus wie bei mir.

Lazarus ist sehr verwandt mit Delphi, hat aber den grossen Vorteil, das es Platformübergreifend ist und erst noch gratis ist.
Lazarus hat noch den Vorteil, das man bei OpenGL das ganze Zeugs mit DC sparen kann, dank dem das es OpenGLControl gibt.

Installiere doch einfach mal Lazarus, unter Windows sollte dann mein Demo ohne weiteren Einstellungen laufen.

_________________
OpenGL


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jun 04, 2018 09:48 
Offline
DGL Member

Registriert: Mo Jul 17, 2006 13:16
Beiträge: 69
Vorläufig scheine ich das Problem gelöst zu haben, die weitere Entwicklung muss zeigen, ob es hält.

TL;DR: Bei jeder Prozedur deines GL-Objektes sicherstellen, dass der DC / RC korrekt sind!

Ich verwende ja für alle meine Objekte eine Ableitung meines "TglObject" (class of TComponent). Dieses bietet nicht nur eine vererbbare "Render"-Prozedur, sondern auch die Möglichkeit, die Werte für den DeviceContext und RenderContext meines Formulars (also des eigentlichen OpenGL-Bereichs) abzuspeichern.

Code:
  1.   TglObject = class(TComponent)
  2.   public
  3.     DC: HDC;
  4.     RC: HGLRC;
  5.     procedure Render;
  6.  


Sobald ich das glObject bei meinem Formular in die Liste der zu rendernden Objekte aufnehme, wird der DC/RC gesetzt (siehe Unten: TglForm_Template.AddglObject).

Bisher habe ich ausschließlich beim Rendern drauf geachtet, dass DC und RC korrekt sind, da meine Annahme war: Selbst wenn ich mit mehreren OpenGL-Formularen arbeite, die alle mit TTimer rendern, wird immer erst eine Render-Prozedur abgeschlossen, bevor die eines anderen Formulars beginnt. Somit sollte es eigentlich beim Zeichnen ansich keine Probleme geben. Dumm ists halt nur, wenn ich z.B. im meinem Nicht-OpenGL-GUI die Schriftgröße eines glText ändere und nun wissen möchte, wie groß der Text in px nun ist: es wird ein Renderdurchgang simuliert und dann abgebrochen, damit die Höhe korrekt berechnet werden kann. Wenn hier -bei dieser "unsichtbaren" Berechnung- der DC/RC nicht stimmt, fliegt einem Alles um die Ohren...

Lösung des Problems:
Alle meine glObjekte können nun "EnsureCorrectDCRC", das letztendlich sicherstellt, dass auch wirklich der DC / RC aktiv ist, für den/mit dem sie etwas zu tun haben. Da "ActivateRenderContext" --> "wglMakeCurrent" sehr zeitintensiv sind, wird wirklich nur gesetzt, falls der Falsche gerade aktiv ist. Auch hier kann es natürlich in Summe zu Verzögerungen/Leistungseinbußen kommen wenn der DC/RC geändert wird, aber das ist dann halt so.
Code:
  1. procedure TglObject.EnsureCorrectDCRC;
  2. begin
  3.   try
  4.     if (DC = 0) or (RC = 0) then begin
  5.       // Kann wären des Initialisieren des Nachfahren von TglObject vorkommen. Fehlerbehandlung nach eigenem Ermessen.
  6.     end;
  7.     if (wglGetCurrentContext <> RC) or (wglGetCurrentDC <> DC) then begin
  8.       wglMakeCurrent(DC, RC);
  9.     end;
  10.  
  11.   except
  12.     on E: SysUtils.Exception do begin
  13.       // Fehlerbehandlung
  14.     end;
  15.   end;
  16. end;
  17.  


Diese Prozedur rufe ich nun immer auf, wenn auch nur der Verdacht besteht, dass in dieser Prozedur meines Objektes irgendwas mit OpenGL gemacht wird, wie gesagt, seit dem ist Ruhe:

-die Texturen des einen Formulars verschwinden nun nicht mehr, wenn das Andere beendet wird
-keine Exceptions oder Fehlermeldungen mehr bei der Programmausführung (speziell Exceptions nur im Graphikkartentreiber)

Eine wichtige Erkenntnis möchte ich noch teilen:
Das Freigeben eines Formulars (also des OpenGL-Bereichs ansich; ich verwende ja immer ein TForm mit OpenGL drauf) ist auch eine OpenGL Aktion! Ergo: korrekten DC/RC sicherstellen!

Das Eingangs beschriebene Problem hing ja auch damit zusammen, dass das freizugebende Formular das Erste in den Tod gerissen hat. Ursache war logischerweise auch (da das freizugebende Formular ja schon ein paar Zyklen nicht mehr rendert), dass durch den Rendervorgang des noch aktiven Formulars DESSEN DC/RC gerade aktiv war. Wenn ich nun "DeactivateRenderingContext;" aufrufe(-n muss), wird halt das erste Formular freigegeben... Kurzum korrigiert:

Code:
  1.  
  2. destructor TglForm_Template.Destroy;
  3. begin
  4.   Active := False;
  5.   wglMakeCurrent(FDC, FRC);
  6.   FreeAndNil(timRenderTimer);
  7.  
  8.   // ...
  9.  
  10.   try
  11.     try
  12.       // Renderkontext deaktiveren
  13.       DeactivateRenderingContext;
  14.       // Renderkontext "befreien"
  15.       wglDeleteContext(FRC);
  16.       // Erhaltenen Gerätekontext auch wieder freigeben
  17.       ReleaseDC(Handle, FDC);
  18.     except
  19.  
  20.     end;
  21.   finally
  22.     inherited;
  23.   end;
  24. end;


Wenn man das so liest ist das natüüüüüürlich selbsterklärend. Wenn man das aber nicht weiß (mehrere DC/RC ist ja scheinbar doch ein ungewöhnlicher Sonderfall), muss man erst mal wissen, wonach man suchen soll. Evtl. sollte es (wglMakeCurrent(DC, RC) vor DeactivateRenderingContext) im Delphigl-Template https://wiki.delphigl.com/index.php/Archiv:template_delphi_vcl mit aufgenommen werden, kann ja eigentlich nicht schaden...?

Auf jeden Fall danke an @mathias für die Bemühungen!


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jun 04, 2018 15:11 
Offline
DGL Member
Benutzeravatar

Registriert: Di Apr 29, 2008 18:56
Beiträge: 1213
Programmiersprache: Delphi/FPC
Hey,

du kannst für einen OpenGL Kontext mehrere TextSuite Kontexte erstellen (auch wenn es nicht sinnvoll ist). Du solltest aber unter keinen Umständen einen TextSuite Kontext mit mehreren OpenGL Kontexten nutzen. Das funktioniert zwar ohne Exceptions (solange du den richtigen OpenGL Kontext sicherstellst), kann aber zu total unvorhersehbaren Verhalten führen. Zum Beispiel werden ja Texturen die in OpenGL Kontext 1 erstellt wurden auch mit diesem Kontext gelöscht. In der TextSuite würden aber weiterhin Referenzen auf diese Textur enthalten sein.

Das beste wäre, du machst dir 2 OpenGL Kontexte und jeweils einen TextSuite Kontext pro OpenGL Kontext. Die TextSuite Objekte darfst du aber nicht zwischen den TextSuite Kontexten austauschen. Wenn du einen Fonz aus Kontext 1 in Kontext 2 benötigst, dann musst du diesen mit den selben Settings auf dem anderen Kontext neu erstellen!

Gruß Bergmann89.

_________________
Aktuelle Projekte: BumpMapGenerator, Massive Universe Online
Auf meiner Homepage gibt auch noch paar Projekte und Infos von mir.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jun 04, 2018 21:15 
Offline
DGL Member

Registriert: Mo Jul 17, 2006 13:16
Beiträge: 69
Hallo @Bergmann89,
vielen Dank dass Du dich meldest, ich wollte Dich schon fast genau deswegen per PM anschreiben.

Die Kernfrage ist: Wie erstelle ich denn mit Deiner TextSuite 2.0 einen (Render)Context?

Code:
  1. unit HAL1_GRAPHICS_glTextSuiteFont;
  2.  
  3. interface
  4.  
  5. {$REGION 'uses 1.1'}
  6. uses
  7.   , dglOpenGL
  8.   , glBitmap
  9.   , uglcContext
  10.   , utsTypes, utsUtils, utsConstants, utsPostProcessor
  11.   , utsTextSuite
  12.   , HAL1_GRAPHICS_FontSettings
  13.   ;
  14. {$ENDREGION}
  15.  
  16.  
  17. type
  18.   TglTextSuiteFont = class(TComponent)
  19.   private
  20.     tsCreate: TtsFontCreatorGDI;
  21.     ftsPostProcessList: TtsPostProcessorList;
  22.     FtsFont: TtsFont;
  23.     procedure ReCreateFontFromFontSetting(Sender: TObject);
  24.   public
  25.     FontSettings: TFontSettings;
  26.     FtsRenderer: TtsRendererOpenGL;
  27.     ftsContext  : TtsContext;
  28.     property tsFont: TtsFont read FtsFont;
  29.     procedure Assign(_Source: TPersistent); override;
  30.     constructor Create(_Owner: TComponent); override;
  31.     destructor Destroy; override;
  32.   end;
  33.  
  34. implementation
  35.  
  36. uses
  37.   gnugettext
  38. , HAL0_Log
  39. , HAL1_GRAPHICS_Font
  40. , HAL1_GRAPHICS_Color
  41. ;
  42.  
  43. const
  44.   M='HAL1_GRAPHICS_glTextSuiteFont';
  45.  
  46. procedure TglTextSuiteFont.Assign(_Source: TPersistent);
  47. var
  48.   Source: TglTextSuiteFont;
  49. begin
  50.   inherited Assign(_Source);
  51.  
  52.   if not assigned(_Source) then Exit;
  53.  
  54.   if _Source.InheritsFrom(TglTextSuiteFont) then begin
  55.     Source := TglTextSuiteFont(_Source);
  56.     FontSettings.Assign(Source.FontSettings); // Löst bereits OnChange aus --> Recreate
  57.   end;
  58. end;
  59.  
  60. constructor TglTextSuiteFont.Create(_Owner: TComponent);
  61. begin
  62.   inherited;
  63.   FtsFont := NIL;
  64.  
  65.   tsCreate := NIL;
  66.  
  67.   ftsContext  := TtsContext.Create;
  68.   ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatRGBA8);
  69.  
  70.   FontSettings := TFontSettings.Create(Self);
  71.   FontSettings.OnChange := Self.ReCreateFontFromFontSetting;
  72.   ReCreateFontFromFontSetting(Self);
  73. end;
  74.  
  75. destructor TglTextSuiteFont.Destroy;
  76. begin
  77.   FreeAndNil(FtsFont);
  78.   FreeAndNil(ftsPostProcessList);
  79.   FreeAndNil(tsCreate);
  80.   FreeAndNil(ftsRenderer);
  81.   FreeAndNil(FtsContext);
  82.  
  83.   inherited;
  84. end;
  85.  
  86. procedure TglTextSuiteFont.ReCreateFontFromFontSetting(Sender: TObject);
  87. var
  88.   tmpFontFileName: string;
  89.   tmpStyle: TtsFontStyles;
  90.   r, g, b: Byte;
  91.   pp: TtsPostProcessor;
  92. begin
  93.   if not assigned(FtsContext) then Exit;
  94.  
  95.   if assigned(FtsFont) then begin
  96.     FreeAndNil(FtsFont);
  97.   end;
  98.  
  99.   if assigned(ftsPostProcessList) then begin
  100.     while ftsPostProcessList.Count > 0 do begin
  101.       pp := ftsPostProcessList.Items[0];
  102.       ftsPostProcessList.Remove(pp);
  103. //      FreeAndNil(pp); // erledigt die zeile davor schon
  104.     end;
  105.     ftsPostProcessList.Clear;
  106.  
  107.     FreeAndNil(ftsPostProcessList);
  108.   end;
  109.  
  110.   FreeAndNil(tsCreate);
  111.  
  112.   if not assigned(tsCreate) then begin
  113.     tsCreate := TtsFontCreatorGDI.Create(ftsContext);
  114.   end;
  115.  
  116.   tmpStyle := [];
  117.   if FontSettings.Bold then begin
  118.     tmpStyle := tmpStyle + [tsStyleBold];
  119.   end;
  120.   if FontSettings.Italic then begin
  121.     tmpStyle := tmpStyle + [tsStyleItalic];
  122.   end;
  123.   if FontSettings.Underline then begin
  124.     tmpStyle := tmpStyle + [tsStyleUnderline];
  125.   end;
  126. //  if fsStrikeOut in Font.Style then begin
  127. //    tmpStyle := tmpStyle + [tsStyleStrikeout];
  128. //  end;
  129.  
  130.   tmpFontFileName := HAL1_Font_GetFileNameFromFontName(FontSettings.FontName);
  131.   FtsFont := tsCreate.GetFontByFile(pAnsiChar(ansistring(tmpFontFileName)), abs(FontSettings.FontSize), tmpStyle, tsAANormal);
  132.  
  133.   if (FontSettings.BorderWidth > 0) or (FontSettings.ShadowWidth > 0) then begin
  134.     ftsPostProcessList := TtsPostProcessorList.Create(ftsContext, true);
  135.  
  136.     if FontSettings.BorderWidth > 0 then begin
  137.       HAL_Color2RGB(0, r, g, b);
  138.       pp := TtsPostProcessorBorder.Create(ftsContext, FontSettings.BorderWidth / 1, 1, tsColor4f(r/255, g/255,b/255, 1), true);
  139.       ftsPostProcessList.Add(pp);
  140.     end;
  141.  
  142.     if FontSettings.ShadowWidth > 0 then begin
  143.       HAL_Color2RGB(clBlack, r, g, b);
  144.       pp := TtsPostProcessorShadow.Create(ftsContext, FontSettings.ShadowWidth / 1, 1, tsPosition(FontSettings.ShadowOffsetX, FontSettings.ShadowOffsetY), tsColor4f(r/255, g/255,b/255, FontSettings.ShadowAlpha/100));
  145.       ftsPostProcessList.Add(pp);
  146.     end;
  147.  
  148.  
  149.     FtsFont.PostProcessor := ftsPostProcessList;
  150.  
  151.   end;
  152.  
  153. end;
  154.  
  155.  
  156. end.


Ich verwende zwar hier TtsContext, aber außer "CodePage" und "DefaultChar" scheint das nicht viel mit einem RenderContext zu tun zu haben?

Zur Info: Vor jeder Arbeit mit der TextSuite mache ich mittlerweile immer das angesprochene
Code:
  1.     if (wglGetCurrentContext <> RC) or (wglGetCurrentDC <> DC) then begin
  2.       wglMakeCurrent(DC, RC);
  3.     end;
  4.  


Weil ich schon früher Probleme mit mehrern DCs/RCs hatte, erzeuge ich ja hier (um sicher zu gehen) für jede Schriftart einen eigenen TtsContext. Ist das Richtig? Ist Schlecht? Hat TtsContext überhaupt was mit dem "TextSuite Kontext" zu tun, oder ähnelt das nur vom Namen hat aber nix gemeinsam? Wie setze ich denn den TextSuite Kontext mit Deiner Objekt orientierten Textsuite-Variante, und wann _muss_ ich auf jeden Fall im richtigen OpenGL-Context sein, bevor ich _was_ mit der TextSuite mache?

Wo ich dich gerade dranhabe und das ja letztendlich auch zum Thema gehört: Bitte schau Dir nochmal
Code:
an, ob man da nicht was machen kann...

Danke vielmals!


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Jun 05, 2018 08:52 
Offline
DGL Member
Benutzeravatar

Registriert: Di Apr 29, 2008 18:56
Beiträge: 1213
Programmiersprache: Delphi/FPC
Hey,

der TtsContext ist der TextSuite 2.0 Kontext, in diesem Kontext werden intern alle möglichen Sachen gespeichert (unter anderem alle Objekte die jemals für diesen Kontext erstellt wurden). Kleine Korrektur zum letzten Post: Es reicht ein TextSuite Kontext für die ganze Anwendung (mehrere gehen zwar auch, aber das erhöt den Verwaltungs-Overhead und unter Umständen die Zeit die beim Rendern benötigt wird).

Der TtsCreatorXXX sorgt dafür, das die Zeichen aus einem Font geladen und in ein Format konvertiert werden, dass die TextSuite versteht. Hier reicht es auch, wenn du einen Creator für die ganze Anwendung erstellst. Der Creator muss auch so lange vorhanden sein, solange du mit den Fonts arbeiten willst. Wenn du den Creator löschst, dann werden auch alle Fonts gelöscht, die mit dem Creator erstellt wurden.

Der TtsRendererOpenGL sorgt dafür, das die von der TextSuit verwalteten Fonts mit OpenGL gezeichnet werden können. Hier könnte man auch andere Renderer implementieren (zum Beispiel für OpenGL ES, oder Direct3D, usw). Der Renderer verwaltet die OpenGL Objekte (Texturen, VBOs, usw) und die einzelnen Glyphen der Fonts, die gerendert werden sollen. Es ist also zwingend erforderlich, das du für jeden OpenGL Kontext einen eigenen Renderer erzeugst und auch sicherstellst, das der jeweils richtige OpenGL Kontext aktiv ist, bevor du irgendetwas mit dem Renderer machst.

Die TextSuite verwaltet alle ihre Objekte intern selbst und gibt diese ggf. frei wenn das übergewordnete Objekte gelöscht wurde. Deshalb hier nochmal die Objekt-Hierarchie:
Code:
  1. TtsContext
  2.     TtsFontCreatorXXX
  3.         TtsFont
  4.             TtsCharCache
  5.     TtsRendererXXX
  6.         TtsTextBlock
  7.         TtsCharCache (ist der selbe wie beim Font)

TtsCharCache speichert die gerenderten Glyphen zwischen (der wird beim ersten Render Aufruf gefüllt). Wenn du jetzt also zum Beispiel deinen FontCreator frei gibst, dann wird mit ihm der Font abgeräumt und mit dem Font wird dann der Char Cache abgeräumt, somit hat der Renderer nichts mehr was er Zeichnen kann. Zweites Beispiel: Du gibst den Renderer frei und der sorgt dafür das alle noch offenen Text Blöcke und die in ihm gecachten Glyphen (CharCache) abgeräumt werden (also müssen die beim erneuten Rendern wieder aus dem Font geladen und gecacht werden).

Den anderen Post hab ich schon gesehen. Ich nehme an, dass du da mit deinen Renderern durcheinander gekommen bist und er irgendwann die falsche oder eine ungültige Textur nutzt. Prüf nochmal ob du die Sachen die ich oben beschrieben habe so eingehalten hast. Wenn es dann immer noch nicht geht, dann gucken wir nochmal wo da das Problem ist.

Gruß Bergmann89.

p.s.: Die TtsPostProcessorList kümmert sich selbst darum das ihre Objekte abgeräumt werden. Ein FreeAndNil(ftsPostProcessList) reicht also um die Liste und alle Objekte in der Liste zu löschen.

_________________
Aktuelle Projekte: BumpMapGenerator, Massive Universe Online
Auf meiner Homepage gibt auch noch paar Projekte und Infos von mir.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 10 Beiträge ] 
Foren-Übersicht » Programmierung » Einsteiger-Fragen


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 23 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.092s | 19 Queries | GZIP : On ]