Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update DisplayDriverServer and ClientDisplayDriver #1448

Open
wants to merge 2 commits into
base: RB-10.5
Choose a base branch
from

Conversation

LinasBeres
Copy link

Adds option to server and client driver to allow a 'merge' driver that shares a display driver to write to.

Create a map that is shared between server sessions that links a session id to a display driver. This allows multiple client drivers to write to the same display driver and thus the same catalogue in Gaffer.

I haven't added tests yet, but does the code look good so far?

Checklist

  • I have read the contribution guidelines.
  • I have updated the documentation, if applicable.
  • I have tested my change(s) in the test suite, and added new test cases where necessary.
  • My code follows the Cortex project's prevailing coding style and conventions.

Copy link
Member

@johnhaddon johnhaddon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Linas!

The general approach here seems good - it's pleasing to see that you've found a way of dropping this into the existing setup with really pretty minimal changes to the code.

I've made a bunch of inline comments with small suggestions for improvements, but nothing that changes the overall strategy. In addition to those and adding some comprehensive tests, it would be good to document the new parameters somewhere, possibly in DisplayDriverServer.h.

Cheers...
John

if (mergeDriverData && mergeDriverData->readable())
{
const IntData *sessionIdData = parameters->member<IntData>( "sessionId", true /* throw if missing */ );
tmpParameters->writable()[ "clientPID" ] = new IntData( sessionIdData->readable() );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to override the clientPID parameter with something that is no longer the process ID of the client? I can't see how it is necessary, for two reasons :

  1. Since only one server-side driver actually gets created from the batch of session clients, that driver will end up with a unique clientPID anyway.
  2. Gaffer no longer cares what the clientPID is - it now uses gaffer:renderID to determine when two drivers are from the same render - spoofing that is going to be a separate problem, and not really one that should be dealt with here.

Maybe I missed something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I think you're right. I'll remove references to it.

@@ -70,6 +72,8 @@ IE_CORE_DEFINERUNTIMETYPED( DisplayDriverServer );
namespace
{

std::map<int, std::pair<DisplayDriverPtr, int>> mergeMap;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use g_ prefixes to denote global variables.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also be tempted to use a little struct with named field for the driver and client count, as first and second become a bit meaningless when you're far away from the point the thing was defined.

Comment on lines 121 to 122
bool m_mergeSession;
int m_mergeId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest std::optional<int> instead of two separate members here.

{
m_displayDriver->imageClose();
}
else if ( auto search = mergeMap.find(m_mergeId); search != mergeMap.end() && --mergeMap[m_mergeId].second <= 0 )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a matter of taste perhaps, but I don't think packing an assignment, a conditional test and a mutation of another variable into if() makes for readable code. I also think it's unnecessary.

The search should never fail, because if we have a merge session, we really must have created the map entry already. If we haven't, or we've deleted it already, that would be a bug, and we don't want a silent failure. Also having done the search with find(), we're doing another map search, because that's what mergeMap[m_mergeId] does internally. I'd suggest simplifying to this :

else
{
    auto &m = mergeMap.at( m_mergeId ); // Throws if not found, alerting us to the bug
    ...
}

Alternatively, we could store the map iterator as member data, instead of m_mergeId.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of storing the iterator.

I agree that having a loud failure would be better.


// Check if merge ID in map, if not then create display driver and session count pair with merge ID.
if (const auto search = mergeMap.find(m_mergeId); search == mergeMap.end())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, I'm not a fan of packing a lot of stuff into an if(), although in this case I suppose it does limit the scope of the search variable, which is nice.

But as above, we're doing more map searches than necessary - find()does a search, then emplace() does a search and then [] does a search. Since in all cases we want to end up with an entry in the map, we could just do this :

auto &m = mergeMap[m_mergeId];
if( !m.first )
{
    // First driver
    m.first = DisplayDriver::create(
    ...
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know enough about DisplayDriverServer to know if different sessions get handled on different threads. If they do, then we need to consider thread-safety for all accesses to the mergeMap. Do you know if this is an issue or not, or is everything on a single thread?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding the server runs as in a separate thread to the rest of cortex, but handles all accepts in a single threaded manner. In which case we wouldn't have to worry about thread-safety.

// Check if merge ID in map, if not then create display driver and session count pair with merge ID.
if (const auto search = mergeMap.find(m_mergeId); search == mergeMap.end())
{
const IntData *sessionClientsData = parameters->member<IntData>( "sessionClients", true /* throw if missing */ );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this parameter tells us how many total clients there are going to be in this session, and it is the responsibility of the person setting up the clients to set it correctly?

I did wonder if we could remove the need for this by just incrementing the client count each time we add a client to the merge. But I suppose it is possible for one very slow machine (or a farm resource allocation problem) to mean that one client doesn't even start before all the others have finished.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is the responsability of the client code to set the parameter up properly.

And you're right I did think about an iterator somewhere, but it could be the case that all but one machine finishes, whilst one machine hasn't even started yet, which would cause the driver to close, and hence the catalogue image.

@@ -423,9 +437,39 @@ void DisplayDriverServer::Session::handleReadOpenParameters( const boost::system

const StringData *displayType = parameters->member<StringData>( "remoteDisplayType", true /* throw if missing */ );

const BoolData *mergeDriverData = parameters->member<BoolData>( "mergeDriver", false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest omitting the mergeDriver parameter completely as it doesn't seem to add anything. We could just use the existence of sessionId to enable the merging behaviour.

}
else
{
m_mergeId = parameters->member<IntData>( "sessionId", true /* throw if missing */ )->readable();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter list for displays is full of all sorts of stuff we've accumulated over the years, and it's not always clear what is used by who, and when. Perhaps we could call this displayDriverServer:mergeId to make it clearer who consumes it, and what it means? And perhaps we could do similar with sessionClients?

Linas Beresna added 2 commits December 17, 2024 12:37
Adds option to server and client driver to allow a 'merge' driver
that shares a display driver to write to.
@ivanimanishi ivanimanishi force-pushed the linasb_allowServerToMergeDrivers branch from e69edc6 to 6104274 Compare December 17, 2024 21:06
@LinasBeres LinasBeres changed the base branch from main to RB-10.5 December 17, 2024 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants