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!