Set rgba channel in RenderWindow

@Dan_Cascaval We gave up on the SetValues() as you might understand from the comments above. I believe in the end what matters is how many times the buffer will be copied around.

Since we are rendering on GPU, DrawOpenGL() made sense as we can get the buffer from our renderer and simply copy it straight into the Rhino OpenGL viewport. Meaning one single copy call and the buffer never leaves the GPU, I can’t give you a number on how long it takes but I’m pretty sure it’s negligible. The limiting factor is usually that Rhino has a limit on 60 fps for redraw.

Not sure why @nathanletwory had crashes but we have not seen any instability using this approach. I understand though that it’s not fit for every usecase, as you need to be on GPU, can’t use post effects and have to supply your own depth data for accurate overlay.

I got a C# implementation of DrawOpenGL() working nicely. I don’t know if we’ll end up using it, but it’s a lot more performant and might help out anyone else hitting issues with SetValues.

private uint _textureId = 0;

public override bool DrawOpenGl()
{
    if (RenderEngine is null)
        return false;

    var initialized = OpenGL.Initialized;
    if (!initialized)
    {
        OpenGL.Initialize();

        glEnable(GL_TEXTURE_2D);

        var textureIds = new uint[1];
        glGenTextures(1, textureIds);
        _textureId = textureIds[0];

        glBindTexture(GL_TEXTURE_2D, _textureId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (int)GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (int)GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (int)GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (int)GL_CLAMP);
    }

    glBindTexture(GL_TEXTURE_2D, _textureId);
    var RGBA = GL_RGBA;
    unsafe
    {
        lock (RenderEngine.DisplayBufferLock)
        {
            // assumes floats 0-1, use byte* and GL_UNSIGNED_BYTE if bytes 0-255
            fixed (float* p = RenderEngine.DisplayBuffer)
            {
                glTexImage2D(GL_TEXTURE_2D, 0, (int)RGBA, _width, _height, 0, RGBA, GL_FLOAT, (IntPtr)p);
            }
        }
    }

    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, -1.0f); // Bottom-left
    glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, -1.0f);  // Bottom-right
    glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, 1.0f);   // Top-right
    glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, 1.0f);  // Top-left
    glEnd();

    return true;
}

It uses the bindings from ghgl, but I added a few more functions to hit this use case:

// class OpenGL
      
public static void Initialize()
{
  // ...
  _glBegin = (glBeginProc)procBuilder.GetProc<glBeginProc>();
  _glEnd = (glEndProc)procBuilder.GetProc<glEndProc>();
  _glVertex2f = (glVertex2fProc)procBuilder.GetProc<glVertex2fProc>();
  _glTexCoord2f = (glTexCoord2fProc)procBuilder.GetProc<glTexCoord2fProc>();
  // ..
}

delegate void glBeginProc(GLenum primitiveMode);
static glBeginProc _glBegin;
public static void glBegin(GLenum primitiveMode)
{
    _glBegin(primitiveMode);
}

delegate void glTexCoord2fProc(GLfloat x, GLfloat y);
static glTexCoord2fProc _glTexCoord2f;
public static void glTexCoord2f(GLfloat x, GLfloat y)
{
    _glTexCoord2f(x, y);
}

delegate void glVertex2fProc(GLfloat x, GLfloat y);
static glVertex2fProc _glVertex2f;
public static void glVertex2f(GLfloat x, GLfloat y)
{
    _glVertex2f(x, y);
}

delegate void glEndProc();
static glEndProc _glEnd;
public static void glEnd()
{
    _glEnd();
}

and

// class WindowsGL 

[DllImport(OPENGL_LIB)]
public static extern void glBegin(GLenum primitiveMode);

[DllImport(OPENGL_LIB)]
public static extern void glTexCoord2f(GLfloat x, GLfloat y);

[DllImport(OPENGL_LIB)]
public static extern void glVertex2f(GLfloat x, GLfloat y);

[DllImport(OPENGL_LIB)]
public static extern void glEnd();

Since we don’t do the main rendering via OpenGL, it is incurring a small overhead to copy from the device and back to the texture, but this seems to be plenty fast enough given Rhino is only refreshing at 60fps regardless. Everything is running quite smoothly now.

For future readers: I went on a bit of a rabbit hole with OpenTK and wglGetCurrentContext, but that seems not to be necessary since it’s set in whatever thread is calling – the only setup the needs doing is setting up the PInvokes with the ghgl bindings. OpenTK is a nicely typed API, and it seems like one could implement their IBindingsContext interface using a similar method to how the ghgl bindings find a delegate for a procedure name, but I was running into issues with it using more functions than I had bound, so punted on that for now. If somebody finds a concise way to do this - let me know!

3 Likes

RH-83576 is fixed in Rhino 8 Service Release 13

1 Like