What’s Next for Cyril
There’s a lot I want to do with Cyril. Some of these things are tracked in the Cyril Issue Queue, some are partially coded up in my own hacked versions of Cyril, and some exist only in my imagination.
Some of these are big, fundamental changes to how Cyril is built. I considered starting from scratch with everything I learned while building Cyril 0.0 and everything I’ve learned since. But, I decided not to do this. As big as some of the changes are, I didn’t want to just throw away the work that had been done (even if, eventually, most of it will have to change).
I often think of this quote from respected software engineer, Kent Beck:
for each desired change, make the change easy (warning: this may be hard), then make the easy change
Some of the changes I want to make are big and complicated. But I’ve been thinking about what I can do to make those changes easier.
With such a big wish-list and to-do list, I needed some priorities about what to work on, and how to approach some of the tasks. After a long, hard think about this I came up with the following priorities:
- make it cross-platform
- make it perform better
- make it easier to hack and/or extend
I’ll review each of these items first, then at the end summarise the current phase of development.
The very first Cyril experiment was on a Raspberry Pi. But, now it only supports OSX. It is tied into an Xcode build system, and links with some OSX only features like Syphon. It also has some platform specific code, such as the copy and paste code.
I don’t want to use Xcode any more. The IDE I have switched to is cross platform, and uses CMake. I hope moving to CMake will make it easier to get a cross-platform build system up and running. I’ve enjoyed learning CMake, and using Git Submodules for dependency management.
openFrameworks has separate build systems for each platform it supports.
My experiments have shown many areas where Cyril’s performance can be improved. The way it operates at the moment is simple, but very wasteful. Improving performance will allow more complex visuals to be built up, or allow Cyril to run on less powerful machines.
The big job here is to change the way Cyril programs are evaluated. Currently, the language parser builds an AST and then on each frame the tree is evaluated (crawled) to render the output. My experiments have shown that a much better option is to compile the AST to a linear structure, rather than a tree. CPUs are much better at iterating over a structure that is linear in memory (caching etc). The resulting Cyril compiled program will use value objects (structs) rather than pointers, so allow placement as contagious block of memory.
I have been re-learning C++ and the modern way to write C++. There are lots of new features introduced in C++11 and C++14. I have got a better understanding on languages features like generic programming and when not to use OO. openFrameworks (on which Cyril is based) makes heavy use of run-time polymorphism. I have a feeling that most of this can be replaced with static polymorphism. For example, Cyril only really needs one keyboard callback, not a whole events system. And, the main update/draw loop that happens every frame is dispatched dynamically at run-time as CyrilApp is a sub-class of openFramework’s baseApp. But, the information of what App is used is known at compile time. I have a feeling like the time saving here could be worth replacing with static polymorphism, and I’ve been working on a small experiment to test this theory.
Hack-ability and extensibility
Cyril has a few components: the language parser, the evaluation and 3d rendering, some other bits that do FFT of audio input, the text editor, OSC, Syphon, PostProcessing. This is all built on top of openFrameworks and openFrameworks add-ons. There is very tight coupling between all these components.
Cyril’s current architecture makes adding new functionality hard. I would love to have a module or plugin-based system where new language features could be easily added in. I’m looking at ways to achieve this in C++ and how this could work with the evaluation and rendering system and the language parser.
My initial ideas are based around separating the language parsing from the evaluation. There are some issues with the language parser (built with Bison and has shift/reduce conflicts). It has hard-coded keywords and names in the grammar of the language. A few tweaks to the language could mean that plugins could provide language features, and the implementation of these features could be decoupled from a core Cyril VM.
What does that mean for the current development?
The top priority is to get a build working on Linux, and hopefully the Raspberry Pi. I want to move away from Xcode.
As I refactor the code, I will probably move away from being so tightly coupled with openFrameworks.
I’m pulling together the OSX and Linux build systems from openFrameworks in the hope to get them working from the same CMake based build system. I’ve made some progress on this. I’ve got the CMake version to build on OSX and started adapting it for Ubuntu.
Thinking about Kent Beck’s quote from above, this is the “make the change easy” stage. I’m not actually changing how Cyril works at this stage, but moving things around to make later changes easier.