Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Klassendesign für Multi-Render Engine (https://www.delphipraxis.net/178124-klassendesign-fuer-multi-render-engine.html)

Zacherl 18. Dez 2013 18:27

Klassendesign für Multi-Render Engine
 
Hallo zusammen,

ich bin momentan dabei eine Engine zu schreiben, mit der man diverse visuelle Komponenten rendern kann. Hierbei würde ich gerne sowohl verschiedene DirectX Frameworks (9, 10, 11), als auch OpenGL und evtl. GDI(+) unterstützen.

Nun stehe ich allerdings etwas auf dem Schlauch, was die Konzeption meiner Klassen angeht. Zuerst einmal: Jede Komponente soll sich selbst rendern; es gibt also momentan eine Methode "Paint", die immer dann aufgerufen wird, wenn die Komponente neu gezeichnet werden muss.

Jetzt habe ich mir verschiedene Lösungswege überlegt:
  1. Abstrakte Render Klasse, die Funktionen wie z.b. DrawRect, FillRect, etc. zur Verfügung stellt.
    • :arrow: Dies wäre eine universelle Lösung, die allerdings den Nachteil hat, dass ich in der Darstellung der Komponente sehr eingeschränkt werde. Ich könnte beispielsweise keine DirectX11 sepzifischen Effekte aufrufen, ohne zusätzlich die abstrakte Render Klasse um entsprechende Funktionen zu erweitern (die dann für alle anderen Frameworks sinnlos wären).
  2. Seperate Paint Methode für jedes unterstüzte Render Framework.
    • :arrow: Hier kann ich mit maximaler Flexibilität (für das jeweilige Framework spezifisch) zeichnen, müsste aber die Zeichenroutinen doppelt und dreifach implementieren.

Hinzu kommen noch weitere Besonderheiten z.b. muss ich bei DX9 auf OnDeviceLost und OnDeviceReset reagieren.

Hat jemand vielleicht noch eine Idee, die mir bisher nicht gekommen ist. Oder welche der Lösungen würdet ihr implementieren?

Viele Grüße
Zacherl

Namenloser 18. Dez 2013 18:42

AW: Klassendesign für Multi-Render Engine
 
An so etwas ähnlichem arbeite ich auch schon eine Weile, nur mit OpenGL und Graphics32. Kann zu einem echten Mammutprojekt ausarten, das nur als Warnung :wink:. Letzter Stand war bei mir, dass ich einen Algorithmus entwickeln musste, um ein komplexes Polygon in Dreiecke zu zerlegen. Für „einfache“ Polygone geht das noch leicht (jedenfalls wenn man sich mit quadratischer Laufzeit zufrieden gibt), aber wenn die Polygone in sich verschlungen sein können oder Löcher enthalten können, dann wird es recht kompliziert... leider hatte ich die letzten Monate keine Zeit, daran weiterzuarbeiten.

Was die von dir beschriebene Konzeption angeht: Ich würde das über Interfaces lösen bzw. habe das in der alten Version meiner Engine (die aktuelle schreibe ich gerade von Grund auf neu und bin da noch nicht so weit) auch so gemacht.

Delphi-Quellcode:
IPrimitivePainter = interface
  procedure DrawRect(x1,y1,x2,y2: float);
  procedure DrawLine(x1,y2,x2,y2: float);
end;

IFancyPainter = interface
  procedure DrawFancyEffect1(...);
  procedure DrawfancyEffect2(...);
end;

I3DPainter = interface
  procedure DrawCube(...);
end;
So in der Art.

Und dann im Quellcode mit
Delphi-Quellcode:
Supports()
prüfen, ob das Interface vom konkreten Painter implementiert wird.

Zacherl 18. Dez 2013 19:28

AW: Klassendesign für Multi-Render Engine
 
Das entspricht ja von der Art her ein wenig der Idee, abstrakte Render / Painter Klassen zu verwenden. Mich stört daran, dass man hier ein wenig an Flexibilität verliert bzw. um einen speziellen Effekt einzubauen, erst die Painter Klasse erweitern muss, um dann die entsprechende Funktion in der Paint Methode der Komponente aufrufen zu können.

Übergibst du dann jeder Komponente einen speziellen Painter, oder benutzt du eine gemeinsames Painter Objekt für alle Komponenten?

Ich überlege grade eine Art Zwischenlösung einzubauen:
  • Abstrakte Painter Klasse / Interface nach deinem Schema, die aber nur ein paar wirklich essentielle Grundfunktionen implementiert
  • Und zusätzlich soll das aktuelle Device, Canvas, etc. in der Paint Methode der Komponenten zur Verfügung stehen, damit man bei Bedarf noch spezielle Eigenschaften des Render Frameworks nutzen kann

Namenloser 18. Dez 2013 20:45

AW: Klassendesign für Multi-Render Engine
 
Zitat:

Zitat von Zacherl (Beitrag 1240290)
Mich stört daran, dass man hier ein wenig an Flexibilität verliert bzw. um einen speziellen Effekt einzubauen, erst die Painter Klasse erweitern muss, um dann die entsprechende Funktion in der Paint Methode der Komponente aufrufen zu können.[/LIST]

Nein, muss man nicht. Ich hatte mich aber gerade vertan, die Funktion heißt QueryInterface, nicht Supports:

Delphi-Quellcode:
procedure TMyFancyButton.Draw(Painter: IPainter);
var
  GradientPainter: IGradientPainter;
begin
  if Painter.QueryInterface(IGradientPainter, GradientPainter) = S_OK then
    GradientPainter.FillGradient(BoundsRect, clWhite, clBlue)
  else
    Painter.FillRect(BoundsRect, clblue);
end;
Man sollte nur vielleicht gucken, dass man die Funktion nicht jedes mal aufruft, weil natürlich jedes mal ein neues Interface erzeugt und wieder zerstört wird, was etwas ineffizient werden könnte.

Furtbichler 18. Dez 2013 22:18

AW: Klassendesign für Multi-Render Engine
 
Vielleicht sollte man doch aus Performancegründen eine Canvas-Fassade erstellen, die alle nötigen Grundfunktionen bereitstellt, wovon es ja nicht so viele gibt.

Damit wäre die Schnittstelle geklärt: Die Parameter sollten in Strukturen/Klassen übergeben werden, um diese erweitern zu können, ohne die Canvas-Schnittstelle anfassen zu müssen.

Die Fassade ist etwas monolithisch und geht in Richtung God-Class, aber ich kann mir vorstellen, das man das einerseits verkraften kann (ist ja eh nur eine leere Hülle ohne Funktion) und muss (Performance).

Man kann diese ICanvas-Interface ja auch explizit für jede Grafik-Engine umsetzen, wie man lustig ist.

Also: Entweder eine Canvas-Klasse und dahinter die Verzweigung in die einzelnen Grafiklibraries, oder für jede Lib/Engine eine eigene Implementierung.


Zitat:

Zitat von Zacherl (Beitrag 1240284)
...die allerdings den Nachteil hat, dass ich in der Darstellung der Komponente sehr eingeschränkt werde. Ich könnte beispielsweise keine DirectX11 sepzifischen Effekte aufrufen, ohne zusätzlich die abstrakte Render Klasse um entsprechende Funktionen zu erweitern (die dann für alle anderen Frameworks sinnlos wären).

Ich fände es eher sinnlos, wenn Controls in den unterschiedlichen Renderen unterschiedlich aussehen.

Man kann ja wohl jeden Effekt in jeder Implementierung emulieren, oder?

PS: Wie willst du das eigentlich umsetzen, wenn Du in DirectX11 einen SuperDuper-Effekt aufrufst, aber für die anderen nicht? Wo soll die Verzweigung denn sein? In deiner Paint-Methode? Oder willst Du die Logik des Zeichnens (was ja nicht ganz ohne ist) in jeder spezifischen Painter-Methode individuell umsetzen?

Wenn man zusammenfasst und abstrahiert muss man alles über einen Kamm scheren. Entweder nimmt man den kleinesten gemeinsamen Nenner, was dann zu einer ziemlich unpotenten Krücke wird, oder man orientiert sich an ein paar Fancy-Gimmicks, die nicht überall unmittelbar verfügbar sind. Alpha-Blending z.B. kann man wunderbar per Hand emulieren, ebenso antialiasing und diverse andere Effekte: Das das vielleicht langsam wird, ok. Aber das kann man immer noch optimieren.

blackfin 18. Dez 2013 23:34

AW: Klassendesign für Multi-Render Engine
 
Wenn du für jede unterstützte Grafikschnittstelle von vornherein alle möglichen Besonderheiten und Möglichkeiten unterstützen willst und diese evtl. sogar iim Programmcode der Applikation selbst "switchen" willst, kann das auch ziemlich in der Sackgasse enden, da man die Übersicht verliert und der eigentliche Programmcode von Branches zugemüllt wird.

Ich würde das wohl eher in zwei Schritten machen:

1) Erst einmal den "kleinsten gemeinsamen Teiler" aller Grafikschnittstellen ermitteln, für alle Primitive und "Objekte", die du unterstützen willst und setzt diese auf die am effizientesten mögliche Art und Weise in separaten Klassen um, die nach aussen jedoch eine konsistente API an die Wrapper-Klasse / deine Engine liefern, z.B. eben Routinen wie DrawPolygon DrawRect oder Subklassen wie new FragmentShader etc.

2) Hast du diese implementiert und bekommst schon mal auf allen Schnittstellen ein ähnliches Bild, dann kannst du die entsprechenden Draw-Routinen für
jede Schnittstelle individuell auch noch mit Schnittellen-spezifischen Befehlen aufhübschen oder erweitern.

Anders machen es die "großen" Engines auch nicht, es wird für jede Schnittstelle einfach das "Ziel" individuell in den Low-Level-Routinen / Klassen umgesetzt, der eigentliche Spiele- / Applikationscode nutzt jedoch einen Layer darüber, der über Interfaces arbeitet, die sicherstellen, dass nach aussen die API für alle unterstützten Schnittstellen exakt gleich ist.

Das hat auch den Vorteil, für die Zukunft auch zusätzliche Grafikschnittstellen einbinden zu können, ohne dich beim Programmcode mit der eigentlichen, darunterliegenenden Schnitstelle befassen zu müssen oder Code am Programm selbst ändern zu müssen.
Willst du z.B. so etwas wie Mantle später unterstützen, baust du dir dafür "einfach" (haha!) eine Klassenstruktur, die deine Engine-API abbildet und du musst nichts am Programm selbst ändern.

Die Grafikschnittellen-spezifischen Optionen würde ich eher nutzen, um entweder performance-technisch die ein oder andere Schnittstelle zu optimieren oder um einige Sachen einfach "schöner" als in der anderen Schnittstelle erscheinen zu lassen.
Z.B. sagt dein Programm nur "new Snow()" o.Ä. Wie der Schnee dann aber aussieht und welche Schnittstellen-spezifischen Effekte er benutzt, kannst du in der eigentlichen Klasse schreiben, die direkt auf die Grafikschnitstelle zugreift.

Zacherl 18. Dez 2013 23:57

AW: Klassendesign für Multi-Render Engine
 
Danke euch für eure Antworten :thumb: Denke, dann weiß ich jetzt erstmal, welche Richtung ich hier einschlagen sollte.

Mavarik 19. Dez 2013 01:10

AW: Klassendesign für Multi-Render Engine
 
Hi!

Für was willst Du den Dein Framework benutzen?

Games?

Mavarik

Zacherl 19. Dez 2013 01:20

AW: Klassendesign für Multi-Render Engine
 
Zitat:

Zitat von Mavarik (Beitrag 1240306)
Für was willst Du den Dein Framework benutzen?

Unter anderem. Soll aber generell nicht an einen bestimmten Zweck gebunden sein.

Mavarik 19. Dez 2013 01:26

AW: Klassendesign für Multi-Render Engine
 
Zitat:

Zitat von Zacherl (Beitrag 1240308)
Zitat:

Zitat von Mavarik (Beitrag 1240306)
Für was willst Du den Dein Framework benutzen?

Unter anderem. Soll aber generell nicht an einen bestimmten Zweck gebunden sein.

OK... Was spricht gegen Firemonkey?


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:56 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz