Shared Memory



  • Hallo alle zusammen 🙂
    Ich habe eine Anwendung in OpenGL geschrieben und möchte die Pixel einer Textur aus dieser Anwendung mit einer anderen C++ Anwendung teilen.Ist dies möglich und wenn ja wo sollte ich dort anfangen ?



  • Wenn du boost benutzt kannst du die Interprocess Bibliothek benutzen.
    Ansonsten bieten sich, je nach Plattform, unterschiedliche Möglichkeiten an (TCP/IP, Pipes, WM_COPYDATA unter Windows, und noch ein paar mehr).
    Der C++ Standard bietet im Moment keine Möglichkeit.



  • Hm. Kann das nicht OpenGL von sich aus? Also mit der passenden Extension halt, aber ohne dass man dazu selbst mit Shared-Memory rummachen müsste.

    Ansonsten würde ich eher mal bei der Shared-Memory Implementierung der Boost schauen. Weil wieso IPC nehmen wenn man eigentlich Shared-Memory möchte?



  • Vielen Dank schon mal für die antworten 🙂 Also Ich würde die Schnellste Methode benutzen und sollte möglichst Plattform unabhängig sein 🙂 Von Boost habe ich schon gehört aber ist es damit möglich ein Pixel Array oder ein Texturbuffer in den Speicher zu schreiben und diesen dann genau so wieder auszulesen in der anderen Anwendung um meine Textur zu rendern ? 🙂

    EDIT: Mir ist leider so eine Funktion in OpenGL nicht bekannt 😕



  • Na gut, dann erwähne ich halt CreateFileMapping, OpenFileMapping und MapViewOfFile für den Fall, dass du für Windows programmierst. Und für den Fall, dass der erzeugende Prozess das SeCreateGlobalPrivilege nicht besitzt brauchst du möglicherweise auch noch CreateBoundaryDescriptor, DeleteBoundaryDescriptor, CreatePrivateNamespace, OpenPrivateNamespace und ClosePrivateNamespace. Details dazu gibt´s wenn klar ist, für welche Plattform du entwickelst und ob du boost benutzen kannst.



  • @xDennis0811
    Das hängt von der Datenstruktur ab. Wenn es ein zusammenhängender Speicherblock ist sollte das ohne großen Aufwand zu erledigen sein, ansonsten musst du dir eine Serialisierung/Deserialisierung überlegen.



  • Ich habe mal in die Boost Library reingeschaut und werde später mal versuchen diese einzubinden dann in der ersten Anwendung Shared Memory zu erstellen, in diesen reinschrieben und in der anderen Anwendung versuchen draus zu lesen 🙂 Hoffe mal das funktioniert ohne große Probleme und dass Ich dann auch noch heraus finde wie Ich eine Textur rein schreibe und wieder Auslese 🙂



  • Also nochmal kurze Informationen 🙂 Mit der ersten Anwendung ist mein Programm mit dem Chromium Embedded Framework gemeint indem ich einfach nur einen Browser erstelle und diesen dann in einen buffer schreibe und in der zweiten Anwendung welche OpenGL zum rendern benutzt wollte ich diesen buffer auslesen und die Textur dann rendern.Hoffe dies ist soweit möglich 🙂



  • Also Ich habe mich mal etwas erkundigt und würde lieber die native shared memory funktion von Windows benutzen.Kann mir da jemand ein Beispiel geben ? 🙂



  • @xDennis0811
    Voíla

    Wie in dem Artikel beschrieben braucht der erzeugende Prozess das SeCreateGlobalPrivilege, das muss man für den Prozess aktivieren. Möglicherweise hat der erzeugende Prozess aber nicht die benötigten Rechte für das Privileg, dann kannst du den Shared Memory Block nicht im Global Namespace erzeugen. Stattdessen kannst du dann einen privaten Namespace erzeugen und darin den Shared Memory Block. Aber probier´ das erst einmal im Global Namespace, wenn das nicht geht reden wir weiter 😉





  • Okay habe es jetzt mit Boost gemacht, was jedoch sehr groß ist um die Anwendung an andere zu versenden aber erstmal nebensächlich.Habe es mit 2 Anwendungen versucht und es probiert reibungslos.Ich muss jetzt nur noch irgendwie herausfinden wie Ich einen Buffer mit Pixeln in den Shared memory schreibe 🙂

    Hauptklasse:

    void createSharedMemory()
    {
    	shared_memory_object::remove("CEFRender");
    	managed_shared_memory managed_shm{ open_or_create, "CEFRender", 1024 };
    	typedef allocator<char, managed_shared_memory::segment_manager> CharAllocator;
    	typedef basic_string<char, std::char_traits<char>, CharAllocator> string;
    	string *s = managed_shm.find_or_construct<string>("BufferTest")("", managed_shm.get_segment_manager());
    }
    

    Zweite Anwendung:

    #include <iostream>
    
    // SHARED MEMORY UTILS
    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/containers/string.hpp>
    #include <iostream>
    
    using namespace boost::interprocess;
    
    int main()
    {
    	managed_shared_memory managed_shm{ open_or_create, "CEFRender", 1024 };
    	typedef allocator<char, managed_shared_memory::segment_manager> CharAllocator;
    	typedef basic_string<char, std::char_traits<char>, CharAllocator> string;
    	std::cout << *managed_shm.find<string>("BufferTest").first << '\n';
    
    	system("pause");
    }
    
    


  • Ich probiere jetzt schon ewig herum aber kriege es nicht hin.Wie bekomme ich denn nun meine Textur in den Shared Memory um diese Wieder auslesen zu können ? Binde sie ganz normal mittels cpp glBindTexture(GL_TEXTURE_2D, web_core.lock()->render_handler()->tex()); ein 🙂



  • @xDennis0811
    Fehlermeldung?
    Ist das, was tex() zurückliefert denn ein Stück zusammenhängender Speicher? Und ist der kleiner als 1024 Byte?



  • Keine Fehlermeldung und nein die Methode liefert nur einen GLint wert zurück undzwar die gebundene TexturID 🙂 Ich glaube diese Methode ist hilfreicher:

    void RenderHandler::OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height)
    {
    	glBindTexture(GL_TEXTURE_2D, tex_);
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, (unsigned char*)buffer);
    	glBindTexture(GL_TEXTURE_2D, 0);
    }
    

    Soweit Ich das richtig verstehe enthält dieser Buffer die Pixel der Browsertextur und speichert diese dann mittels glTexImage2D() in eine Textur ab.Aber wie könnte ich diesen buffer in den Shared Memory schreiben ? 🙂



  • Wenn buffer der Zeiger auf die Pixeldaten ist, dann kannst du anhand der Höhe, Breite und Pixel ausrechnen, wie groß dieser Puffer ist. Aus GL_RGBA schließe ich 32 Bit per Pixel, also ist der Puffer width * height * 4 Bytes groß. Und die kannst du direkt in den shared_memory block kopieren.
    Aus dem Beispiel der boost-Dokumentation (alles ungetestet):

    void create_shm_block( const void* buffer, std::size_t size )
    {
       shared_memory_object shm ( create_only, "CEFRender", read_write );
       shm.truncate( size );
       mapped_region rgn( shm, read_write );
       std::memcpy( rgn.get_address(), buffer, size );
    }
    

    Der zweite Prozess greift nur lesend auf den shared_memory Block zu:

    void read_shm_block()
    {
       shared_memory_object shm ( open_only, "CEFRender", read_only );
       mapped_region rgn( shm, read_only );
    
       const char* buffer = reinterpret_cast<const char*>( rgn.get_address() );
    }
    

    Damit werden nur die Texturdaten kopiert, die Informationen über BPP, Höhe und Breite gehen verloren. Aber die kannste ja vor die Pixeldaten schreiben, dann musste den shared_memory Block eben um die Größe von drei integer vergrößern.



  • Ich werde den Code gleich mal probieren vielen Dank 🙂 Oder ich erstelle einfach Variablen für width, height und bpp da zumindest width und height konstant sind und bpp soweit ich weiß auch 🙂



  • Okay also Ich denke der Shared memory wird auf jeden Fall beschrieben und auch ausgelesen von der anderen Anwendung aber die Größe des SM soll 8 sein, was eigentlich nicht sein kann bei einer width und height von 512.
    Hier ist meine RenderHandler Klasse:

    // Ŭnicode please
    #include "render_handler.h"
    #include <iostream>
    
    // SHARED MEMORY UTILS
    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/containers/string.hpp>
    #include <boost/interprocess/streams/bufferstream.hpp>
    
    using namespace boost::interprocess;
    
    
    using namespace std;
    
    RenderHandler::RenderHandler()
    	: width_(2), height_(2), tex_(0)
    {
    }
    
    
    
    void RenderHandler::init()
    {
    	glGenTextures(1, &tex_);
    	glBindTexture(GL_TEXTURE_2D, tex_);
    
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    	// dummy texture data - for debugging
    	const unsigned char data[] = {
    		255, 0, 0, 255,
    		0, 255, 0, 255,
    		0, 0, 255, 255,
    		255, 255, 255, 255,
    	};
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    
    	glBindTexture(GL_TEXTURE_2D, 0);
    }
    
    void create_shm_block(const void* buffer, std::size_t size)
    {
    	shared_memory_object::remove("CEFRender");
    	shared_memory_object shm(create_only, "CEFRender", read_write);
    	shm.truncate(size);
    	mapped_region rgn(shm, read_write);
    	std::memcpy(rgn.get_address(), buffer, size);
    }
    
    void RenderHandler::resize(int w, int h)
    {
    	width_ = w;
    	height_ = h;
    }
    
    void RenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
    {
    	rect = CefRect(0, 0, width_, height_);
    }
    
    void *texture_data;
    
    void RenderHandler::OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height)
    {
    	glBindTexture(GL_TEXTURE_2D, tex_);
    
    	create_shm_block((unsigned char*) buffer, width * height * 4);
    
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, (unsigned char*)buffer);
    	glBindTexture(GL_TEXTURE_2D, 0);
    }
    

    Und die Zweite Anwendung:

    #include <iostream>
    
    // SHARED MEMORY UTILS
    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/containers/string.hpp>
    #include <iostream>
    
    using namespace boost::interprocess;
    
    int main()
    {
    	shared_memory_object shm(open_only, "CEFRender", read_only);
    	mapped_region rgn(shm, read_only);
    
    	while (true)
    	{
    		const char* buffer = reinterpret_cast<const char*>(rgn.get_address());
    
    		std::cout << sizeof(buffer) << std::endl;
    	}
    	
    
    	system("pause");
    }
    

    Habe ich irgendwas übersehen ? 😕



  • @xDennis0811
    Jau, hast du.

    Problem 1:
    In RenderHandler::init() erzeugst du eine Dummy-Texture der Größe 2x2 und dem Pixel-Datentyp GL_UNSIGNED_BYTE. Deine Texture hat damit einen Speicherbedarf von 4 Byte. Von deinem const unsigned char data[] Array (das 16 Elemente hat) werden nur die ersten vier für die Texture benutzt. Ist jetzt kein echtes Problem aber man sollte schon drauf achten, dass zusammen gehörende Daten korrekte Werte haben.
    Das Ausgaberechteck hat vielleicht eine Größe von 512x512 Pixeln, aber das hat nichts mit der Textur zu tun.

    Problem 2:
    In deiner zweiten Anwendung gibst du die Größe des Zeigers aus, nicht die Größe des Blocks, auf den buffer zeigt. Wenn du da den Wert 8 erhälst bist du wahrscheinlich auf einem 64bit System unterwegs, weil Zeiger dort idR eine Größe von 64bit haben. Es gibt keine Chance nur aus einem Zeiger die Größe des Blocks zu bestimmen, auf den der Zeiger zeigt. Da musst du selbst ein paar Informationen bereitstellen. mapped_region::get_size() bietet eine Möglichkeit, die Größe des Datenblocks zu bestimmen. Allerdings hilft dir das nicht weiter, weil du nicht weisst, wie hoch, wie breit und wie viele Bitplanes deine Textur hat.
    Bsp: Der Block ist 256.000 Bytes groß. Das kann entstanden sein durch:
    640x400 Pixel bei 8bit Farbtiefe
    320x200 Pixel bei 32bit Farbtiefe
    64000x2 Pixel bei 16bit Farbtiefe

    Deshalb musst du diese Informationen mit in den shared_memory Block schreiben. Am besten baust du dir sowas wie

    // Anweisung an deinen Compiler auf Byte-Grenzen auszurichten (compilerspezifisch!)
    // #pragma pack( push, 1 )
    struct TextureDescriptor
    {
        uint32_t Width = 0;
        uint32_t Height = 0;
        uint8_t  BitsPerPixel = 0; // oder GLEnum für den zu Grunde liegenden Pixel-Datentyp
    };
    // Alte Ausrichtungseinstellung wiederherstellen
    //#pragma pack( pop )
    static_assert( sizeof( TextureDescriptor ) == 9, "Size of TextureDescriptor must be 9 Byte!" );
    

    Und schreibst das vorneweg in den shared_memory Block, gefolgt von den Texturdaten. Du musst natürlich die Größe des shared_memory um die Größe der Struktur vergrößern, damit das alles in den Block passt. Mit den Daten kannst du dann auch einen Plausibilitätstest durchführen. Es muss
    rgn.get_size() = ( Width * Height * BitsPerPixel / 8 ) + 9 sein. Wenn das nicht aufgeht steht Murks in deinem shared_memory Block.



  • Okay habe es nun erfolgreich geschafft die Textur in einer anderen Anwendung zu rendern vielen dank 🙂 Es gibt nur noch ein Problem und zwar wird die Textur in der zweiten Anwendung nicht aktualisiert, z.B wenn Ich im Webbrowser in der ersten Anwendung ein Youtubevideo schaue wird es im ersten Fenster korrekt angezeigt aber im zweiten Fenster bekomme Ich ein Standbild.Habe nun mal in beiden Konsolen den Buffer ausgegeben, in der ersten vor dem kopieren und in der zweite nachdem Ich den shared memory ausgelesen habe und der buffer in der zweiten Anwendung bleibt tatsächlich immer gleich.
    Rendermethode und die Erstellung des SM Blocks in der ersten Anwendung:

    void create_shm_block(void* buffer, std::size_t size)
    {
    	shared_memory_object::remove("CEFRender");
    	shared_memory_object shm(create_only, "CEFRender", read_write);
    	shm.truncate(size);
    	mapped_region rgn(shm, read_write);
    	std::cout << buffer << std::endl;
    	std::memcpy(rgn.get_address(), buffer, size);
    }
    
    void RenderHandler::resize(int w, int h)
    {
    	width_ = w;
    	height_ = h;
    }
    
    void RenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
    {
    	rect = CefRect(0, 0, width_, height_);
    }
    
    void RenderHandler::OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height)
    {
    	glBindTexture(GL_TEXTURE_2D, tex_);
    
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
    
    	create_shm_block((void*) buffer, width * height * 4);
    
    	glBindTexture(GL_TEXTURE_2D, 0);
    }
    

    Rendern des Buffers in der Zweiten Anwendung:

    int main()
    {
    	shared_memory_object shm(open_only, "CEFRender", read_only);
    	mapped_region rgn(shm, read_only);
    
    while (!glfwWindowShouldClose(window))
    	{
    		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    		glUseProgram(prog);
    
    		glm::mat4 mvp = glm::ortho(-1, 1, -1, 1);
    		//mvp *= glm::rotate((float)glfwGetTime() * 10.f, glm::vec3(0, 0, 1));
    		glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, glm::value_ptr(mvp));
    
    		glEnableVertexAttribArray(pos_loc);
    		glEnableVertexAttribArray(texcoord_loc);
    
    		// bind texture
    		glBindTexture(GL_TEXTURE_2D, tex);
    
    		void* buffer = reinterpret_cast<void*>(rgn.get_address());
    
    		std::cout << buffer << std::endl;
    
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
    
    		// draw
    		glVertexAttribPointer(pos_loc, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    		glVertexAttribPointer(texcoord_loc, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
    		glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_SHORT, indices);
    
    		glDisable(GL_TEXTURE_2D);
    		glBindTexture(GL_TEXTURE_2D, 0);
    
    		// render end
    		glfwSwapBuffers(window);
    
    		// SHARED MEMORY TESTS
    
    
    		// update
    		glfwPollEvents();
    	}
    }
    

    Konsolenausgabe der ersten Anwendung:

    000001D1F1069040
    000001D1F1066040
    000001D1F1069040
    000001D1F1064040
    000001D1F1065040
    000001D1F329B040
    000001D1F329D040
    000001D1F1064040
    000001D1F106A040
    000001D1F329F040
    000001D1F329E040
    000001D1F329F040
    000001D1F1061040
    

    Konsolenausgabe der zweiten Anwendung:

    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    0000021C18510000
    

    EDIT: Habe das const void* zu einem void* gemacht weil ich dachte dass dadurch eventuell das Problem gelöst wird 🙂


Log in to reply