Come organizzare la gestione delle risorse per OpenGL?

1

Scrivo il mio motore di grafica 3D per l'educazione e ho qualche difficoltà con la sua architettura. Ho scritto classi come OpenGLTexure, OpenGLMaterial, OpenGLGpuProgram, ecc. Ho anche scritto una classe ResourceManager per caricare trame, shader e materiali.

Ora voglio separare il componente grafico OpenGL del mio motore per aggiungere anche il supporto per DirectX. Ho realizzato interfacce per classi grafiche (ITexture, IMaterial). Ma il mio gestore risorse usa le funzioni OpenGL per caricare e inizializzare le risorse (ad esempio, gli shader) e non so come aggiungere il supporto DirectX con questa architettura giusta. Penso di poter scrivere in classe factory per creare oggetti per ogni API grafica, ma come posso scegliere quale classe dovrei usare.

Come posso risolvere questo problema giusto?

    
posta Nikolai Paukov 06.01.2018 - 16:18
fonte

1 risposta

1

Esistono alcuni modi per controllare quale tipo di risorse restituire. Dipenderà da come un utente della tua libreria utilizza la libreria. Se includono l'origine completa (come fanno Unity e Unreal Engine), il modo più semplice è richiedere a un utente della tua libreria di definire quale sistema stanno usando - OpenGL o DirectX - in fase di compilazione. Potrebbe essere necessario che l'utente definisca alcune macro del preprocessore come questa:

#define IMPLEMENTATION_OPENGL 1

per OpenGL e

#define IMPLEMENTATION_DIRECTX 1

per DirectX.

È necessario che un utente della tua libreria definisca quale di questi utilizzare in alcune intestazioni precompilate. Quando il compilatore ha ottenuto i file della tua libreria, controllerebbe quale compilare, in questo modo:

class ITextureLoader {
    public:
        ITextureLoader(const char* imagePath);
        ~ITextureLoader();
    // ... etc.
};

#if IMPLEMENTATION_OPENGL
class TextureLoaderOpenGL : public ITextureLoader {
    public:
        TetxureLoaderOpenGL(const char* imagePath);
        ~TextureLoaderOpenGL();
    //... etc.
};
#elif IMPLEMENTATION_DIRECTX
class TextureLoaderDirectX : public ITextureLoader {
    public:
        TetxureLoaderDirectX(const char* imagePath);
        ~TextureLoaderDirectX();
    //... etc.
};
#endif

Quindi avresti un TextureLoaderFactory che restituirebbe un oggetto ITextureLoader come questo:

ITextureLoader* CreateTextureLoader(const char* imagePath)
{
    #if IMPLEMENTATION_OPENGL
    return new TextureLoaderOpenGL(imagePath);
    #elif IMPLEMENTATION_DIRECTX
    return new TextureLoaderDirectX(imagePath);
    #endif
}

Se non si include la fonte per gli utenti, è necessario decidere in fase di esecuzione. In questo caso potrebbe essere più semplice creare solo 2 librerie diverse per l'utente. Non è molto frequente che un utente voglia utilizzare sia OpenGL che DirectX nella propria applicazione.

Potresti anche averli creare una sorta di oggetto di contesto che definisce cose come il renderer da usare. Passano quindi quell'oggetto contestuale ai metodi factory che creano gli oggetti concreti. Qualcosa del genere:

enum Technology {
    Technology_OpenGL = 1,
    Technology_DirectX = 2
} Technology;

struct TechnologyContext {
    Technology tech;
    int majorVersion;
    int minorVersion;
};

Quindi la tua fabbrica sarebbe simile a questa:

ITextureLoader* CreateTextureLoader(const TechnologyContext& ctx, const char* path)
{
    if (ctx.tech == Technology_OpenGL)
    {
        return new TextureLoaderOpenGL(path);
    }
    else
    {
        return new TextureLoaderDirectX(path);
    }
}

Come utente, lo troverei più ingombrante, personalmente.

    
risposta data 06.01.2018 - 17:49
fonte