Shared Memory



  • 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 🙂



  • Hallo,

    im jedem OnPaint-Aufruf wird ein neues shared_memory_object/mapped_region erzeugt?
    Das sieht mir sehr verkehrt aus. Das muss doch nur einmal erzeugt werden und nur das memcpy muss wiederholt werden.



  • @Jockelx sagte in Shared Memory:

    im jedem OnPaint-Aufruf wird ein neues shared_memory_object/mapped_region erzeugt?
    Das sieht mir sehr verkehrt aus. Das muss doch nur einmal erzeugt werden und nur das memcpy muss wiederholt werden.

    Genau. Ich würde auf beiden Seiten open_or_create verwenden, und dann zumindest auf der Producer-Seite das Objekt offen halten so lange der Canvas für den Browser existiert.



  • Ich denke, dass man sogar noch einen Mutex drumrum braucht, damit sich Producer/Consumer nicht in die Quere kommen. Ich glaube auch, dass die Destruktoren von shared_memory_object , je nach Erzeugungsmodus, den shared memory Bereich wieder freigeben. Meine Motivation hier zu helfen lässt etwas nach, TE begnügt sich offensichtlich damit Quelltext zu kopieren und dann nach weiterer Hilfe zu fragen.


Anmelden zum Antworten