API agnostik jembatan (mis. OpenGL / D3D / Terserah). Apakah Anda menggunakannya, bagaimana Anda membuatnya. Pro's and Con's [ditutup]

12

Anda membuat mesin 3d. Anda menginginkan yang terbaik dari dunia multi platform. Tiba-tiba Anda menyadari bahwa jika Anda ingin menggunakan Direct3D pada mesin Windows dan OpenGL pada OSX / Linux, Anda harus mengorbankan fitur yang didukung dari keduanya untuk penyebut yang paling tidak umum.

Beberapa mungkin menggunakan OpenGL di tiga OS ', karena tampaknya menjadi penyebut yang paling umum dengan sendirinya. Semua baik. Kemudian, Anda harus mem-porting backend API grafik Anda ke Nintendo GX, Anda juga harus membuat jalur PS3 dan Xbox360.

Apa yang kamu kerjakan? Apakah Anda merancang API Anda sendiri yang merupakan penyebut paling tidak umum dalam dirinya sendiri dan menulis implementasi backend untuknya untuk setiap platform atau apakah Anda menulis untuk setiap platform yang memiliki cabang sendiri?

Jika Anda memilih untuk merancang API Anda sendiri, apakah Anda menggunakan pola jembatan atau voodoo Anda sendiri? Di mana kegilaan berhenti ketika Anda menyadari segalanya dan pendekatan kitchen sink harus berhenti dan Anda pada dasarnya memiliki mesin terpisah untuk setiap platform sebagai cabang. Atau Anda tetap berpegang pada segalanya dan kitchen sink dan menyimpan spesifik platform di spesialisasi modul backend untuk setiap platform.

Bingkai kunci
sumber

Jawaban:

9

Saya bukan penggemar pendekatan denominator yang paling tidak umum. Jika Anda melakukannya, Anda mungkin berakhir dengan fitur yang cacat dan kinerja yang buruk.

Sebaliknya, apa yang telah saya lakukan di masa lalu, adalah untuk menyediakan fungsionalitas tingkat yang sedikit lebih tinggi di perpustakaan. Pustaka itu (kebanyakan) API agnostik dan dapat digunakan di mana saja, tetapi implementasi pustaka sangat berbeda untuk platform / grafis backend yang berbeda. Jadi misalnya, alih-alih memiliki fungsi SetStateX (), Anda memiliki fungsi yang lebih tinggi seperti RenderMesh (), atau CreateRenderTarget ().

Ini akan lebih berfungsi daripada lapisan yang sangat tipis setiap kali Anda pindah ke platform baru, tetapi itu akan sangat berharga karena Anda akan dapat mengimplementasikan hal-hal dengan cara yang optimal untuk platform itu, dan Anda akan dapat mengambil keunggulan fitur asli dan unik.

Satu hal lagi: Jangan takut merusak enkapsulasi. Tidak ada yang lebih menyebalkan bahwa mengetahui Anda berada di platform dengan kemampuan tertentu dan tidak dapat menggunakannya. Membiarkan semacam pintu belakang sehingga kode tingkat yang lebih tinggi dapat memanfaatkan platform ini sangat berguna (misalnya, dapat mengambil perangkat D3D atau konteks OpenGL).

Noel Llopis
sumber
3
Saya pikir Anda mengatakan apa yang saya coba katakan, hanya lebih baik.
AShelly
6

Yang bisa saya katakan adalah melihat pada Ogre3D . Ini ditulis dalam C ++, Open source (Lisensi MIT sekarang) dan berjalan di setiap platform utama di luar kotak. Itu abstrak render api dan dapat beralih dari menggunakan DirectX ke OpenGL hanya dengan beberapa pengaturan. Namun, saya tidak cukup tahu tentang perbedaan antara set fitur DirectX dan OpenGL untuk mengatakan bahwa itu mendukung atau tidak mendukung fitur tertentu.

Torchlight oleh Runic Games ditulis menggunakan Ogre dan saya telah memainkannya di Mac dan PC dan itu berjalan sangat baik di keduanya.

Casey
sumber
2
+1 untuk pendekatan Ogre. Saya tahu tentang hal itu, telah membaca beberapa kode. Saya lebih tertarik mendengar cerita pribadi tentang pendekatan itu dan apa yang dilakukan orang lain dalam situasi seperti itu.
Keyframe
2
Terima kasih! Yah aku akan melakukannya sebagian besar seperti yang dilakukan Ogre. Saya telah menggunakan pendekatan Interface / Factory dalam banyak pengembangan lintas platform dan saya sebenarnya tidak yakin bagaimana saya akan melakukannya sebaliknya. Saya akan mengatakan bahwa Anda benar-benar harus bekerja pada beberapa platform sekaligus. Misalnya, jangan mencoba dan kode semua yang ada di Windows dan jangan coba port ke Mac.
Casey
Iya! Saya semakin lelah dengan menggulung pembungkus lintas-API saya sendiri, dan kemudian saya mulai menggunakan Ogre. Belum melihat ke belakang. :)
jacmoe
1

Saya belum melakukan ini untuk grafis, tapi saya membuat toolkit audio lintas-platform (PC / XBOX / PS2). Kami menempuh rute untuk membuat API kami sendiri dengan kapabilitas denominator yang paling tidak umum serta kapabilitas khusus platform opsional. Inilah beberapa pelajaran yang bisa dipetik:

Kuncinya adalah untuk menentukan jalur pemrosesan yang merangkum kemampuan inti dari setiap platform, dan memungkinkan pertumbuhan. Untuk melakukan ini, Anda harus benar-benar memahami API tingkat rendah setiap platform sehingga Anda dapat mengidentifikasi abstraksi yang tepat. Pastikan rantai bekerja untuk platform yang paling tidak mampu, sambil memberikan akses ke fitur canggih patform yang paling mampu. Lakukan pekerjaan untuk memperbaiki ini dan Anda akan menghemat banyak usaha nanti.

Untuk audio, rantai itu seperti SoundSources -> [Decoders] -> Buffers -> [3D Positioner] ->[Effects] -> Players.

Untuk grafis, mungkin Model -> Shapes -> Positioner -> Texturer -> [Lighting] -> [Particle Effects] -> Renderer. (Ini mungkin set yang benar-benar salah, saya bukan pria grafis).

Tulis API ujung depan yang menangani objek inti Anda, dan ujung belakang platform khusus yang memetakan API ke kemampuan tingkat rendah. Memberikan upaya terbaik untuk setiap kemampuan. Sebagai contoh, pada PC dan XBOX posisi audio 3D dilakukan menggunakan kemampuan HRTF dari chip suara, sementara PS2 menggunakan wajan dan pudar sederhana. Mesin grafis mungkin melakukan sesuatu yang mirip dengan pencahayaan.

Terapkan sebanyak mungkin ujung depan dengan kode netral platform. Kode untuk membelokkan objek reverb ke objek suara, atau aset tekstur ke objek bentuk harus benar-benar umum, seperti halnya kode untuk beralih melalui dan memproses objek aktif. Di sisi lain, objek level rendah dapat sepenuhnya platform spesifik kecuali untuk antarmuka umum.

Pastikan API, atau file konfigurasi, memungkinkan pengguna untuk menentukan opsi spesifik platform. Kami mencoba menghindari mendorong kode khusus platform ke level game dengan menyimpannya dalam file konfigurasi: File konfigurasi satu platform dapat menentukan "efek: SuperDuperParticleGenerator" sementara yang lain mengatakan "efek: SoftGlow"

Jelas mengembangkan platform secara paralel. Pastikan bahwa antarmuka spesifik platform didefinisikan dengan baik dan dapat diuji sendiri. Ini mencegah banyak "apakah itu level platform atau level API?" masalah saat debugging.

ASHelly
sumber
0

Saya sedang menulis mesin permainan sumber terbuka yang disebut YoghurtGum untuk platform seluler (Windows Mobile, Android). Ini adalah salah satu masalah besar saya. Pertama saya memecahkannya seperti ini:

class RenderMethod
{

public:

  virtual bool Init();
  virtual bool Tick();
  virtual bool Render();

  virtual void* GetSomeData(); 

}

Apakah Anda melihat void*? Itu karena RenderMethodDirectDrawmengembalikan permukaan DirectDraw sambil RenderMethodDirect3Dmengembalikan kumpulan titik. Segala sesuatu yang lain terpecah juga. Saya memiliki Spritekelas yang memiliki SpriteDirectDrawpointer atau SpriteDirect3Dpointer. Agak tersedot.

Jadi belakangan ini, saya telah menulis ulang banyak hal. Apa yang saya miliki sekarang adalah a RenderMethodDirectDraw.dlldan a RenderMethodDirect3D.dll. Anda dapat, pada kenyataannya, mencoba menggunakan Direct3D, gagal, dan menggunakan DirectDraw sebagai gantinya. Itu karena API tetap sama.

Jika Anda ingin membuat sprite, Anda tidak melakukannya secara langsung tetapi melalui pabrik. Pabrik kemudian memanggil fungsi yang benar di DLL dan mengubahnya menjadi induk.

Jadi, ini ada di RenderMethodAPI:

virtual Sprite* CreateSprite(const char* a_Name) = 0;

Dan ini adalah definisi dalam RenderMethodDirectDraw:

Sprite* RenderMethodDirectDraw::CreateSprite(const char* a_Name)
{
    bool found = false;
    uint32 i;
    for (i = 0; i < m_SpriteDataFilled; i++)
    {
        if (!strcmp(m_SpriteData[i].name, a_Name))
        {
            found = true;
            break;
        }
    }

    if (!found) 
    {
        ERROR_EXPLAIN("Could not find sprite named '%s'", a_Name);
        return NULL; 
    }

    if (m_SpriteList[m_SpriteTotal]) { delete m_SpriteList[m_SpriteTotal]; }
    m_SpriteList[m_SpriteTotal] = new SpriteDirectDraw();

    ((SpriteDirectDraw*)m_SpriteList[m_SpriteTotal])->SetData(&m_SpriteData[i]);

    return (m_SpriteList[m_SpriteTotal++]);
}

Saya harap ini masuk akal. :)

PS Saya ingin sekali menggunakan STL untuk ini, tetapi tidak ada dukungan pada Android. :(

Pada dasarnya:

  • Simpan setiap render dalam konteksnya sendiri. Baik DLL, atau perpustakaan statis atau hanya sekelompok tajuk. Selama Anda memiliki RenderMethodX, SpriteX dan StuffX Anda emas.
  • Curi sebanyak mungkin dari sumber Ogre.

EDIT: Ya, masuk akal untuk memiliki antarmuka virtual seperti ini. Jika upaya pertama Anda gagal, Anda dapat mencoba metode render lain. Dengan cara ini Anda dapat menyimpan semua metode pembuatan kode Anda agnostik.

knight666
sumber
1
Apakah benar-benar masuk akal untuk memiliki antarmuka virtual jika Anda tidak akan pernah memiliki lebih dari satu implementasi yang aktif secara bersamaan?
NocturnDragon
0

Saya suka menggunakan SDL untuk ini. Ia memiliki backend renderer untuk D3D, OpenGl, OpenGL ES, dan beberapa backend spesifik platform lainnya, dan tersedia untuk semua jenis platform yang berbeda, dan saat ini sedang dalam pengembangan aktif, dengan binding untuk berbagai bahasa yang tersedia.

Ini menghilangkan konsep renderer yang berbeda dan membuat pembuatan video (serta menangani suara dan input dan beberapa hal lainnya) tersedia dalam API lintas platform yang sederhana. Dan itu dirancang oleh Sam Lantinga, salah satu pengembang terkemuka di Blizzard, khusus untuk membuat game porting dan membuat game lintas platform lebih mudah, sehingga Anda tahu Anda berurusan dengan perpustakaan berkualitas tinggi.

Mason Wheeler
sumber