diff --git a/include/IECoreImage/DisplayDriverServer.h b/include/IECoreImage/DisplayDriverServer.h index d66503f8bf..097812d50a 100644 --- a/include/IECoreImage/DisplayDriverServer.h +++ b/include/IECoreImage/DisplayDriverServer.h @@ -67,6 +67,14 @@ class IECOREIMAGE_API DisplayDriverServer : public IECore::RunTimeTyped using Port = int; #endif + struct MergeDriverInfo + { + DisplayDriverPtr mergeDriver = nullptr; + int mergeCount = 0; + }; + + using MergeMap = std::map; + using PortRange = std::pair; /// A port number of 0 causes a free port to be chosen @@ -92,7 +100,6 @@ class IECOREIMAGE_API DisplayDriverServer : public IECore::RunTimeTyped static const PortRange ®isteredPortRange( const std::string &name ); private: - // Session class // Takes care of one client connection. class Session; @@ -104,6 +111,8 @@ class IECOREIMAGE_API DisplayDriverServer : public IECore::RunTimeTyped class PrivateData; IE_CORE_DECLAREPTR( PrivateData ); PrivateDataPtr m_data; + + MergeMap m_mergeMap; }; IE_CORE_DECLAREPTR( DisplayDriverServer ) diff --git a/src/IECoreImage/DisplayDriverServer.cpp b/src/IECoreImage/DisplayDriverServer.cpp index c337225b76..f5291ed5cb 100644 --- a/src/IECoreImage/DisplayDriverServer.cpp +++ b/src/IECoreImage/DisplayDriverServer.cpp @@ -53,6 +53,9 @@ #include "boost/bind/bind.hpp" #include +#include +#include +#include #include #ifndef _MSC_VER @@ -95,7 +98,7 @@ class DisplayDriverServer::Session : public RefCounted { public: - Session( boost::asio::io_service& io_service ); + Session( boost::asio::io_service& io_service, DisplayDriverServer::MergeMap& mergeMap ); ~Session() override; boost::asio::ip::tcp::socket& socket(); @@ -114,6 +117,8 @@ class DisplayDriverServer::Session : public RefCounted DisplayDriverPtr m_displayDriver; DisplayDriverServerHeader m_header; CharVectorDataPtr m_buffer; + DisplayDriverServer::MergeMap& m_mergeMap; + std::optional m_mergeId; }; class DisplayDriverServer::PrivateData : public RefCounted @@ -196,7 +201,7 @@ DisplayDriverServer::DisplayDriverServer( DisplayDriverServer::Port portNumber ) { m_data = new DisplayDriverServer::PrivateData( portNumber ); - DisplayDriverServer::SessionPtr newSession( new DisplayDriverServer::Session( m_data->m_service ) ); + DisplayDriverServer::SessionPtr newSession( new DisplayDriverServer::Session( m_data->m_service, m_mergeMap ) ); m_data->m_acceptor.async_accept( newSession->socket(), boost::bind( &DisplayDriverServer::handleAccept, this, newSession, boost::asio::placeholders::error)); @@ -280,7 +285,7 @@ void DisplayDriverServer::handleAccept( DisplayDriverServer::SessionPtr session, { if (!error) { - DisplayDriverServer::SessionPtr newSession( new DisplayDriverServer::Session( m_data->m_service ) ); + DisplayDriverServer::SessionPtr newSession( new DisplayDriverServer::Session( m_data->m_service, m_mergeMap ) ); m_data->m_acceptor.async_accept( newSession->socket(), boost::bind( &DisplayDriverServer::handleAccept, this, newSession, boost::asio::placeholders::error)); @@ -292,8 +297,8 @@ void DisplayDriverServer::handleAccept( DisplayDriverServer::SessionPtr session, * DisplayDriverServer::Session functions */ -DisplayDriverServer::Session::Session( boost::asio::io_service& io_service ) : - m_socket( io_service ), m_displayDriver(nullptr), m_buffer( new CharVectorData( ) ) +DisplayDriverServer::Session::Session( boost::asio::io_service& io_service, DisplayDriverServer::MergeMap& mergeMap ) : + m_socket( io_service ), m_displayDriver(nullptr), m_buffer( new CharVectorData( ) ), m_mergeMap( mergeMap ) { } @@ -363,7 +368,19 @@ void DisplayDriverServer::Session::handleReadHeader( const boost::system::error_ { try { - m_displayDriver->imageClose(); + if ( !m_mergeId.has_value() ) + { + m_displayDriver->imageClose(); + } + else + { + auto &m = m_mergeMap.at(m_mergeId.value()); // Error out if not found + if ( --m.mergeCount <= 0 ) + { + m_mergeMap.erase(m_mergeId.value()); + m_displayDriver->imageClose(); + } + } } catch ( std::exception &e ) { @@ -424,8 +441,31 @@ void DisplayDriverServer::Session::handleReadOpenParameters( const boost::system const StringData *displayType = parameters->member( "remoteDisplayType", true /* throw if missing */ ); // create a displayDriver using the factory function. - m_displayDriver = DisplayDriver::create( displayType->readable(), displayWindow->readable(), dataWindow->readable(), channelNames->readable(), parameters ); + if ( !parameters->member( "displayDriverServer:mergeId", false ) ) + { + m_displayDriver = DisplayDriver::create( displayType->readable(), displayWindow->readable(), dataWindow->readable(), channelNames->readable(), parameters ); + } + else + { + m_mergeId = parameters->member( "displayDriverServer:mergeId", false /* throw if missing */ )->readable(); + // Check if merge ID in map, if not then create display driver and session count pair with merge ID. + auto &m = m_mergeMap[m_mergeId.value()]; + if ( !m.mergeDriver ) + { + const IntData *sessionClientsData = parameters->member( "displayDriverServer:mergeClients", true /* throw if missing */ ); + m.mergeDriver= DisplayDriver::create( + displayType->readable(), + displayWindow->readable(), + displayWindow->readable(), // For merge we want dataWindow = displayWindow + channelNames->readable(), + parameters + ); + m.mergeCount = sessionClientsData->readable(); + } + // Merge ID is now in map, so load the display driver. + m_displayDriver = m.mergeDriver; + } scanLineOrder = m_displayDriver->scanLineOrderOnly(); acceptsRepeatedData = m_displayDriver->acceptsRepeatedData(); } diff --git a/test/IECoreImage/DisplayDriverServerTest.py b/test/IECoreImage/DisplayDriverServerTest.py index 9bb82a0e8d..ca1a00b4b7 100644 --- a/test/IECoreImage/DisplayDriverServerTest.py +++ b/test/IECoreImage/DisplayDriverServerTest.py @@ -34,12 +34,20 @@ import unittest import sys +import os +import imath import IECore import IECoreImage class DisplayDriverServerTest( unittest.TestCase ) : + def __prepareBuf( self, buf, width, offset, red, green, blue ): + for i in range( 0, width ): + buf[3*i] = blue[i+offset] + buf[3*i+1] = green[i+offset] + buf[3*i+2] = red[i+offset] + def testPortNumber( self ) : s1 = IECoreImage.DisplayDriverServer( 1559 ) @@ -118,6 +126,63 @@ def testPortRangeRegistry( self ) : s2 = IECoreImage.DisplayDriverServer() self.assertEqual( s2.portNumber(), 45021 ) + def testMergeMap( self ) : + server = IECoreImage.DisplayDriverServer( 45001 ) + + img = IECore.Reader.create( os.path.join( "test", "IECoreImage", "data", "tiff", "bluegreen_noise.400x300.tif" ) )() + self.assertEqual( img.keys(), [ 'B', 'G', 'R' ] ) + red = img['R'] + green = img['G'] + blue = img['B'] + width = img.dataWindow.max().x - img.dataWindow.min().x + 1 + + params = IECore.CompoundData() + params['displayHost'] = IECore.StringData('localhost') + params['displayPort'] = IECore.StringData( '45001' ) + params['displayDriverServer:mergeId'] = IECore.IntData( 42 ) + params['displayDriverServer:mergeClients'] = IECore.IntData( 2 ) + params["remoteDisplayType"] = IECore.StringData( "ImageDisplayDriver" ) + params["handle"] = IECore.StringData( "myHandle1" ) + + idd1 = IECoreImage.ClientDisplayDriver( img.displayWindow, img.dataWindow, list( img.channelNames() ), params ) + + params["handle"] = IECore.StringData( "myHandle2" ) + idd2 = IECoreImage.ClientDisplayDriver( img.displayWindow, img.dataWindow, list( img.channelNames() ), params ) + + params['displayDriverServer:mergeId'] = IECore.IntData( 666 ) + params['displayDriverServer:mergeClients'] = IECore.IntData( 1 ) + params["handle"] = IECore.StringData( "myHandle3" ) + idd3 = IECoreImage.ClientDisplayDriver( img.displayWindow, img.dataWindow, list( img.channelNames() ), params ) + + buf = IECore.FloatVectorData( width * 3 ) + for i in range( 0, img.dataWindow.max().y - img.dataWindow.min().y + 1 ): + self.__prepareBuf( buf, width, i*width, red, green, blue ) + idd1.imageData( imath.Box2i( imath.V2i( img.dataWindow.min().x, i + img.dataWindow.min().y ), imath.V2i( img.dataWindow.max().x, i + img.dataWindow.min().y) ), buf ) + idd2.imageData( imath.Box2i( imath.V2i( img.dataWindow.min().x, i + img.dataWindow.min().y ), imath.V2i( img.dataWindow.max().x, i + img.dataWindow.min().y) ), buf ) + idd3.imageData( imath.Box2i( imath.V2i( img.dataWindow.min().x, i + img.dataWindow.min().y ), imath.V2i( img.dataWindow.max().x, i + img.dataWindow.min().y) ), buf ) + + idd1.imageClose() + idd2.imageClose() + idd3.imageClose() + + newImg = IECoreImage.ImageDisplayDriver.removeStoredImage( "myHandle1" ) + newImg2 = IECoreImage.ImageDisplayDriver.removeStoredImage( "myHandle2" ) + newImg3 = IECoreImage.ImageDisplayDriver.removeStoredImage( "myHandle3" ) + + # merge drivers share the same display driver - so second image should be none, + # as there is no image drivere associated with it. + self.assertIsNone( newImg2 ) + + # remove blindData for comparison + newImg.blindData().clear() + img.blindData().clear() + self.assertEqual( newImg, img ) + + newImg3.blindData().clear() + self.assertEqual( newImg3, img ) + + server = None + if __name__ == "__main__": unittest.main()