Spout Receiver in OpenGL C++

Home Forums Spout Developer Spout Receiver in OpenGL C++

Viewing 15 posts - 1 through 15 (of 18 total)
  • Author
    Posts
  • #4054
    meebs
    Participant

    Hi Everyone,

    I have been trying and failing for a few days now to get any signs of life out of my spout receiver in a C++, OpenGL project I’m working on in Visual Studio. I’ve already got an OpenGL program running some shaders and I’m now attempting to add Spout Input to my software. My initial approach was to open a spout receiver like this:

    bool didWork = spoutClient->CreateReceiver(senderName, width, height, shouldUseActive);
        spoutClientInitialized_ = didWork;
        if (!didWork) {
          DevLog::WinLog("Spout sender failed to open. Stopping Spout Input until a new sender is selected.");
          return didWork;
        }
        currentActiveSender_ = senderName;
        spoutInputSharedTexture_ = initGLtexture(width, height);
    
        return didWork;
    

    where initGLtexture is as follows:

      GLuint SpoutClientManager::initGLtexture(unsigned int width, unsigned int height)
      {
        GLuint texID;
        GL(glGenTextures(1, &texID));
        GL(glBindTexture(GL_TEXTURE_2D, texID));
        GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
        GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
        GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
        GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
        GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
        GL(glBindTexture(GL_TEXTURE_2D, 0));
        lastResolutionOfSpoutInputTex_.first = width;
        lastResolutionOfSpoutInputTex_.second = height;
        return texID;
      }

    Then, during my main OpenGL program’s render loop, a function getNewFrame() is called:

    
    void SpoutClientManager::getNewFrame() {
      unsigned int width, height;
      char senderName[256];
     
      bool didWork = spoutClient->ReceiveTexture(senderName,
        width,
        height,
        spoutInputSharedTexture_,
        GL_TEXTURE_2D);
    

    At this point, Spout should have copied it’s shared texture to my spoutInputSharedTexture_ right? I then bind and set this texture to a sampler2D in my shader. However, the texture always remains as it was initialized. For instance, if I wrote blue into it instead of NULL in my glTexImage2D, it would remain blue, even as didWork returns true, suggesting that Spout has correctly received the new texture. I then thought perhaps I should use the less performant but more dirt-simple approach of recieve image.

    So I modified my spout receiver initialization to include:

    
    if (senderbuffer) free((void *)senderbuffer);
        senderbuffer = (unsigned char *)malloc(640*480 * 4 * sizeof(unsigned char));
    

    Note the hardcoded width and height to keep things simple for now. Then in my render loop I end up calling:

        
       bool didWork = spoutClient->ReceiveImage(senderName,
        width,
        height,
        senderbuffer,
        GL_RGBA);
        GL(glBindTexture(GL_TEXTURE_2D, spoutInputSharedTexture_));
        GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, senderbuffer));
        

    At this point, the image should be copied into both senderbuffer and my GL texture. And yes, at least I find that the texture changes from what it was initialized to. However, I end up with a texture that is entirely light grey. In fact, if I print some values out of senderbuffer just to check, I see that every value I print has an integer value of “205”, correctly corresponding to the light grey I see. Nothing I change on the SpoutSender side has any effect on this color, and I get the same behavior regardless of whether I’m using SpoutSender.exe or TouchDesigner as my spout source.

    Finally, I thought I might use the BindSharedTexture() approach. This time I tried:

      bool didWork = spoutClient->ReceiveTexture(senderName,
        width,
        height);
    
      GL(glActiveTexture(GL_TEXTURE10));
      GL(spoutClient->BindSharedTexture());
      GL(glUniform1i(glGetUniformLocation(program_, "spoutTex"), 10));
      GL(spoutClient->UnBindSharedTexture());
      GL(glBindTexture(GL_TEXTURE_2D, 0));
    
    

    Yet again the texture was just black, all the while “didWork” has been returning true indicating Spout success. I’m clearly doing something wrong or missing some fundamental piece. I’ve tried every combination of these approaches with various settings in SpoutDXMode.exe, and tried various Spout senders (SpoutSender.exe and TouchDesigner).

    Do you know what I’m doing wrong here? For the record, I have an NVidia 980M and am running Windows 10. The system does have Intel Iris and Optimus, but I have every program started with “High Performance Graphics Enabled” and the diagnostics from SpoutSender are as follows:

    Spout Sender (Vers 2.018)
    DirectX 9.0c available
    NVIDIA GeForce GTX 980M (Display)
    Microsoft Basic Render Driver (Secondary)
    Current adapter : NVIDIA Corporation
    NV_DX_interop extension available
    DirectX 11 Memory share mode
    • This topic was modified 1 year, 3 months ago by meebs.
    • This topic was modified 1 year, 3 months ago by meebs.
    #4068
    leadedge
    Keymaster

    We are having problems with forum replies right now. It appears that email notifications come through OK but the replies don’t show up on the forum itself. So I am splitting this into separate posts while we figure out what is happening.

    #4069
    leadedge
    Keymaster

    Well the last post came through, so this is the next installment.

    Hi meebs, sorry that you having problems.

    First of all, I see from the diagnostics that the system is running memporyshare mode. This could be due to the Optimus graphics preferring the integrated adapter. Correct Optimus setup is necessary to enable texture sharing and that might need some attention.

    Open SpoutDXmode and check Texture share mode ON. Close it and open again. If texture share mode is still unchecked there is a problem with the Optimus confguration. The drop down list at the bottom sometimes does’nt work and you need to go through the NVIDIA control panel. Get this part going first if you can. The diagnostics from the Spout demo programs should finally show “DirectX 11 Texture share mode”

    If it still remains in memory share mode, just leave it for now, we can look at that later. Spout demo sender should be received OK by the demo receiver in either mode. If it does, your application will too.

    Hopefully I can post some code in the next reply.

    #4070
    leadedge
    Keymaster

    Looks like it’s rejecting something. Working on it.

    #4071
    leadedge
    Keymaster

    No success yet!

    Meanwhile, I guess you have already done so, but have another look at example code on the Spout GitHub repository to see how it compares with your existing code.

    https://github.com/leadedge/Spout2/blob/master/SpoutSDK/Example/ofSpoutExample/src/ofSpoutReceiver/ofApp.cpp

    and a practical example :

    https://github.com/leadedge/Spout2/blob/master/FFGL/Source/SpoutReceiver/SpoutReceiverSDK3.cpp

    #4076
    leadedge
    Keymaster

    No luck in posting the code but it’s really just a copy of the Github examples.

    There has been another report of a black screen unless texture sharing. So the best thing now would be to revisit the original Optimus problem and get the system working in texture share mode.

    #4077
    meebs
    Participant

    Hey leadedge, thanks for the quick replies.

    Memoryshare mode was a last-ditch effort on my part, I enabled it in SpoutDXMode only for testing. For the most part I was running in Texture share mode. Do you recommend I use either DirectX 9 or OpenGL pixel buffering options? I am fairly certain this is not GPU related, as I have used Spout successfully on this machine with other VJ softwares, including output from the very one I’m working on now (just trying to get input working). But good call, I’ll make sure I leave it in Texture mode for the rest of my tests.

    As for the ofApp.cpp example, I’ve looked through that many times but never found it very helpful because I have no OpenFrameworks experience and all the good stuff like getting the texture on the screen and initializing it were hidden behind openframeworks abstractions.

    However, the SpoutReceiverSDK3 example looks like exactly what I need. I’ll try to follow everything they do there. I wish I had found that earlier! I tried looking for any C++ and raw OpenGL examples for a while but couldn’t find any. I didn’t look through the SDK examples well enough.

    I’ll try again with ReceiveTexture, but any idea why ReceiveImage would fail in the way I described? Basically the whole of the received pixel data seems to be written as light grey, int value 205. I’ll try writing to the pixel data to initialize it to red before receiving to ensure it wasn’t just at 205 by default and ReceiveImage actually had no effect. ReceiveImage should be fairly foolproof, right? If the spout client was initialized properly and it returns with a success I’d expect to see some pixel data (perhaps the wrong RGB format, but still not a total blank screen).

    I really appreciate the help.

    #4078
    leadedge
    Keymaster

    OK I understand. There has to be something wrong but I don’t know what, if everything else on the machine works OK.

    To avoid complication, leave the buffering option off then pbos won’t be used for pixel data copy (ReceiveImage for example). Leave the DirectX 9 option off too. It’s generally only useful if there are compatibility problems with the driver.

    Yes ReceiveImage will just fill the pixel buffer with data from the shared (OpenGL linked) texture.

    All the the texture share copy functions use an fbo and framebuffer blit so that the result can be flipped. So it could be related to the fbo. I am thinking about it.

    It’s fairly easy to reproduce the code in the receiver plugin which is what I was trying to post here. I will email you that instead and hopefully we can get to the bottom of this.

    #4112
    meebs
    Participant

    Okay, I got it working, but have no idea why this worked. Adding this line before I receive Texture make it work.
    unsigned int width, height;
    bool connected;
    spoutClient->CheckReceiver(senderName, width, height, connected);
    In the SpoutSDK it says you don’t need to actually use this function, but perhaps a side effect of this function (IE setting some state in the Spout SDK) is what is actually fixing it?

    EDIT: Oh wow. Okay, so ReceiveTexture needs Width and Height set manually in order to work. I thought Width and Height were outputs of the function, and could be passed in as such:

    unsigned int width, height;
    spoutClient->ReceiveTexture(senderName,
        width,
        height,
        spoutInputSharedTexture_,
        GL_TEXTURE_2D,
        true,
        drawFboId);

    Then width and height would be modified by the function and I could test them after ReceiveTexture to see what size the sender was sending at. (facepalm)

    Instead, these are inputs to the function, apparently to tell ReceiveTexture the size of the OpenGL texture it is going to render to. So I got lucky in calling spoutClient->CheckReceiver(senderName, width, height, connected); and being lazy and reusing the same width and height variables. You may want to clear up in the SDK guide that the width and height define the size of the texture you’ve created. I’m also still confused as to why ReceiveTexture can return a success if I pass in uninitialized or mis-sized width and height, making it fail at copying the texture. However, very happy it is working now and big ups to the Spout devs and Leadedge in particular for being so responsive.

    • This reply was modified 1 year, 3 months ago by meebs.
    • This reply was modified 1 year, 3 months ago by meebs.
    #4114
    leadedge
    Keymaster

    I am not sure what is happening – I will need to trace it through.

    Anyway – the forum is giving all sorts of trouble at the moment and your previous posts of the 9th have been held as pending without any notice to administration. I won’t sort through them but rather enable them all and we can look at the sequence of events.

    #4103
    meebs
    Participant

    Okay, I got your email and am working on cleaning up my code. At this point I’m back to the best approach with ReceiveTexture, proper DXMode settings, still just getting my blue test texture (aka receivetexture claims to have worked, but doesn’t change what is in the texture whatsoever when I bind and render it to screen).

    A few questions:

    When I create a receiver, I have to pass it the sender name I want to receive from. However, when I ReceiveTexture, I also have to pass a sender name, right? How do those two interact? Are you supposed to just make sure you’re passing the same senderName to each of those functions? I realized that for some portion of my testing, although I called CreateReceiver with the proper name, I was sending a blank string to ReceiveTexture. It is odd to me that it still returned “success”, but perhaps this is because it was ignoring my blank string and using the one I set when I called CreateReceiver?

    I’m also confused about this optional FBO you can pass in to ReceiveTexture. What is this FBO supposed to be? Here is my current understanding, can you tell me if this is correct:
    ReceiveTexture needs to set an FBO during its process, changing the underlying OpenGL state, because Spout actually functions within the same GL context as whatever application it is running within. This means that if I had previously bound an FBO to render to, I’ll need to rebind it. Alternately, I can pass in the FBO as an optional argument to this function and Spout will call glBindFramebuffer(GL_FRAMEBUFFER, myFBO)); so I don’t have to call it. Is me passing in an FBO and me calling this line right after ReceiveTexture completely equivalent? During my render code, I always call glBindFramebuffer right before glDrawArrays, so I shouldn’t need to pass in any FBO right?

    #4105
    meebs
    Participant

    Okay since the above posted but my previous one still isn’t there, here it is again:

    Okay, I got your email and am working on cleaning up my code. At this point I’m back to the best approach with ReceiveTexture, proper DXMode settings, still just getting my blue test texture (aka receivetexture claims to have worked, but doesn’t change what is in the texture whatsoever when I bind and render it to screen).

    A few questions:

    When I create a receiver, I have to pass it the sender name I want to receive from. However, when I ReceiveTexture, I also have to pass a sender name, right? How do those two interact? Are you supposed to just make sure you’re passing the same senderName to each of those functions? I realized that for some portion of my testing, although I called CreateReceiver with the proper name, I was sending a blank string to ReceiveTexture. It is odd to me that it still returned “success”, but perhaps this is because it was ignoring my blank string and using the one I set when I called CreateReceiver?

    I’m also confused about this optional FBO you can pass in to ReceiveTexture. What is this FBO supposed to be? Here is my current understanding, can you tell me if this is correct:
    ReceiveTexture needs to set an FBO during its process, changing the underlying OpenGL state, because Spout actually functions within the same GL context as whatever application it is running within. This means that if I had previously bound an FBO to render to, I’ll need to rebind it. Alternately, I can pass in the FBO as an optional argument to this function and Spout will call glBindFramebuffer(GL_FRAMEBUFFER, myFBO)); so I don’t have to call it. Is me passing in an FBO and me calling this line right after ReceiveTexture completely equivalent? During my render code, I always call glBindFramebuffer right before glDrawArrays, so I shouldn’t need to pass in any FBO right?

    #4106
    meebs
    Participant

    Not sure what’s going on with the forum but here are my questions split into single paragraph posts so that they can post successfully, I hope. 1) When I create a receiver, I have to pass it the sender name I want to receive from. However, when I ReceiveTexture, I also have to pass a sender name, right? How do those two interact? Are you supposed to just make sure you’re passing the same senderName to each of those functions? I realized that for some portion of my testing, although I called CreateReceiver with the proper name, I was sending a blank string to ReceiveTexture. It is odd to me that it still returned “success”, but perhaps this is because it was ignoring my blank string and using the one I set when I called CreateReceiver?

    #4111
    meebs
    Participant

    Posting single paragraph posts since they seem to work? When I create a receiver, I have to pass it the sender name I want to receive from. However, when I ReceiveTexture, I also have to pass a sender name, right? How do those two interact? Are you supposed to just make sure you’re passing the same senderName to each of those functions? I realized that for some portion of my testing, although I called CreateReceiver with the proper name, I was sending a blank string to ReceiveTexture. It is odd to me that it still returned “success”, but perhaps this is because it was ignoring my blank string and using the one I set when I called CreateReceiver?

    #4120
    meebs
    Participant

    Finally, feel free to delete my other posts (aside from the answer) now that I’ve solved the issue. Sorry for the spam!

Viewing 15 posts - 1 through 15 (of 18 total)
  • You must be logged in to reply to this topic.