After 2 years of rewriting Rubyk and the Oscit library, things are still in “alpha”. This “failure” happens for
two three reasons.
The first is the energy drawn by trying to create a very important part of Rubyk (network layer) as a collaborative effort within an existing community (open sound control). I kept worrying about latency and guaranteed delivery of packets and spent a lot of time discussing array formats and the existence or not of a Hash type.
The second part that took a hell of a lot of time is the design of the dynamic typing, introspection capabilities and garbage collection to cope with the dynamic nature of the signals and objects in Rubyk. Doing all this work ended in a true language design and I do not have the competences nor the commitment (or wish) to do create such a language out of the void.
The third point is a bad understanding of concurrent programming. When I started the whole thing over two years ago, I thought “let’s be ready for multi cores, we need threads”. So I carefully rewrote everything to be thread-safe. This new level of complexity is also hindering progress and I feel it is inherently bad (more on this later).
Doing all these things wrong was very formative and I feel I am much better armed now to make the correct decisions (and the necessary tools are available now).
The first decision was to use zeroMQ for inter-process and network communications. It’s fast enough, packet delivery is guaranteed and I can send whatever I want.
The second decision was to rewrite the core in Lua (or LuaJit where possible) instead of C++. This means that we have a share nothing architecture between threads and that the development will be much easier and agile. We can also delegate all the trouble of dynamic typing, introspection and memory management to Lua.
Lua will be much more fun to work with, can easily be interfaced with any C code and is really fast enough for the “glue” between objects that the Rubyk core provides.
In order to decide between a closures based implementation (uses more memory, elegant) and an implementation based on metatables (method resolution resembling Ruby classes), I did some tests with different implementations of the Outlet/Inlet. The tests are pretty trivial, so please do not consider the results below as anything else then a starting point on the subject.
The memory test creates
100'000 mixer objets (they add their inputs and send to output).
The speed test executes an input+send in a connected graph
|implementation||Lua memory||LuaJit memory||Lua speed||LuaJit speed|
|closures (no self)||95.6 Mb||62.6 Mb||11’821 ms||355 ms|
|metatable||40.4 Mb||29.8 Mb||17’004 ms||323 ms|
The first tests where flawed since the compiler optimized the code to an empty loop. Thanks to Mike Pall (author of LuaJit) for pointing this out.
From the tests (and the remark by Mike), the “metatable” route is the way to go since it uses less memory and enables more optimizations from the LuaJit compiler.
I am confident that these choices will remove what I don’t like in working with Rubyk and let me focus on the pieces of the software that really matter: reusable components, graph design, remote user interface, network transparency, embedding, etc.
Thanks to Lua(jit) and ØMQ for the great projects !
PS: I just need to find or write a simple cross-platform GUI that is easy to build and script (Qt Quick maybe).
Geeez I’ve been waiting for this one. Finally after 39 hours work non-stop (I feel drunk) and the creation of a new ruby gem called Dub (Doxygen based Ubiquitous Binder which generates
10'018 lines of code that compile without a warning, I can PLAY with OpenCV in Rubyk.
The only source for images right now is the video signal, but that quite fun already.
If you want to reproduce the stupid rotating cube with your own face on it, heres the code:
(It is also an example in
# run from build directory with # ./rubyk ../examples/GLLua/opencv.rk win = GLWindow() cv = GLLua("../examples/GLLua/opencv.lua") win => cv video = VideoIn() video => video~cv m = Metro(3000) m => win
The code has been updated to use the new texture based rendering.
require('cv') -- size must be a power of 2 size = cv.Size(128,128) res = res or cv.Mat() n = n or 0 x = x or 0 y = y or 0 z = z or 0 function video(frame) cv.resize(frame, res, size, 0, 0, cv.INTER_LINEAR) frame_changed = true n = n + math.pi / 300 x = math.cos(n / 0.9) * 360 / math.pi y = math.sin(n / 0.7) * 360 / math.pi z = math.sin(n) * 360 / math.pi end inlet('video', MatrixIO('send me images')) function draw() gl.Clear( "COLOR_BUFFER_BIT, DEPTH_BUFFER_BIT") --gl.ClearDepth(1.0) gl.MatrixMode("MODELVIEW") gl.LoadIdentity() if frame_changed then rk.makeTexture(res, tex) frame_changed = false end gl.Translate(0.0, 0.0, -6.0) gl.Rotate(x, 1.0, 0.0, 0.0) gl.Rotate(y, 0.0, 1.0, 0.0) gl.Rotate(z, 0.0, 0.0, 1.0) gl.Color(0.5,0.5,0.0)--,0.3) gl.LineWidth(1.0) glut.WireCube(2.6) gl.Color(0.5,0.5,0.0,0.1) glut.SolidCube(2.6) -- simples way to draw a texture gl.Translate(0.0, 0.0, -1.5) -- do not forget to set a color or nothing will be drawn gl.Color(1,1,1,0.5) rk.drawTexture(tex, 1.3, 1.3, -1.3, -1.3) end function resize(width, height) textures = textures or gl.GenTextures(1) tex = textures gl.Enable("BLEND") gl.Disable("DEPTH_TEST") -- not working ??? gl.BlendFunc("SRC_ALPHA", "ONE_MINUS_SRC_ALPHA") gl.Enable("TEXTURE_2D") gl.Enable("LINE_SMOOTH") -- Select the projection matrix gl.MatrixMode("PROJECTION") -- reset gl.LoadIdentity() -- Calculate the aspect ratio of the view gl.Perspective( 45.0, -- Field of view angle width / height, -- Aspect ration 1.0, -- zNear 100.0 -- zFar ) view.width = width view.height = height end
And here is the code that evaluates the curves and color transitions along the paths:
for t = 0,1.0,(1.0/resolution) do ta = (1-t)^3 tb = 3 * t * (1-t)^2 tc = 3 * (1-t) * t^2 td = t^3 gl.Color( c1.r * (1-t) + c4.r * t, c1.v * (1-t) + c4.v * t, c1.b * (1-t) + c4.b * t ) gl.Vertex( ta * p1 + tb * p2 + tc * p3 + td * p4, ta * p1 + tb * p2 + tc * p3 + td * p4, ta * p1 + tb * p2 + tc * p3 + td * p4 ) end
This will allow us to start working on the project. I am still looking for more funding (budget for the full interface is about 34’000 CHF)... If you know a generous donator or want to help, please announce yourself to the mailing list.
If you have strong competence in interface programming and C++, I am looking for a second programmer to help me in this task. Preferably around Lausanne (CH), so we can work in the same place…
Loading latest twitter data...
Funding from the Swiss Federal Office of Culture to write the graphical frontend to rubyk !
Moving from a global mutex to a global select/poll loop.