AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Klassendesign für Multi-Render Engine

Ein Thema von Zacherl · begonnen am 18. Dez 2013 · letzter Beitrag vom 19. Dez 2013
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#1

Klassendesign für Multi-Render Engine

  Alt 18. Dez 2013, 19:27
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.
    • 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.
    • 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
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#2

AW: Klassendesign für Multi-Render Engine

  Alt 18. Dez 2013, 19:42
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 . 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 Supports() prüfen, ob das Interface vom konkreten Painter implementiert wird.
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: Klassendesign für Multi-Render Engine

  Alt 18. Dez 2013, 20:28
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
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#4

AW: Klassendesign für Multi-Render Engine

  Alt 18. Dez 2013, 21:45
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.
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#5

AW: Klassendesign für Multi-Render Engine

  Alt 18. Dez 2013, 23:18
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.


...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.
  Mit Zitat antworten Zitat
blackfin
(Gast)

n/a Beiträge
 
#6

AW: Klassendesign für Multi-Render Engine

  Alt 19. Dez 2013, 00:34
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.

Geändert von blackfin (19. Dez 2013 um 01:03 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#7

AW: Klassendesign für Multi-Render Engine

  Alt 19. Dez 2013, 00:57
Danke euch für eure Antworten Denke, dann weiß ich jetzt erstmal, welche Richtung ich hier einschlagen sollte.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.143 Beiträge
 
Delphi 10.3 Rio
 
#8

AW: Klassendesign für Multi-Render Engine

  Alt 19. Dez 2013, 02:10
Hi!

Für was willst Du den Dein Framework benutzen?

Games?

Mavarik
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#9

AW: Klassendesign für Multi-Render Engine

  Alt 19. Dez 2013, 02:20
Für was willst Du den Dein Framework benutzen?
Unter anderem. Soll aber generell nicht an einen bestimmten Zweck gebunden sein.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.143 Beiträge
 
Delphi 10.3 Rio
 
#10

AW: Klassendesign für Multi-Render Engine

  Alt 19. Dez 2013, 02:26
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?
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:16 Uhr.
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