mediaMachineAPI.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_API_MEDIA_MACHINE
  24. #define DFPSR_API_MEDIA_MACHINE
  25. #include "../image/Image.h"
  26. #include "../base/Handle.h"
  27. #include "../math/FixedPoint.h"
  28. namespace dsr {
  29. // A handle to a media machine.
  30. // Media machines can be used to generate, filter and analyze images.
  31. // Everything running in a media machine is guaranteed to be 100% deterministic to the last bit.
  32. // This reduces the amount of code where maintenance has to be performed during porting.
  33. // It also means that any use of float or double is forbidden.
  34. struct VirtualMachine;
  35. using MediaMachine = Handle<VirtualMachine>;
  36. // TODO: Complete VirtualMachine with conditional jumps and document the language dialect used by MediaMachine.
  37. // Side-effect: Creates a media machine from Media Machine Code (*.mmc file).
  38. // Post-condition: Returns a reference counted MediaMachine handle to the virtual machine.
  39. MediaMachine machine_create(const ReadableString& code);
  40. // Post-condition: Returns true iff machine exists.
  41. bool machine_exists(const MediaMachine& machine);
  42. // Pre-condition:
  43. // * The machine must exist.
  44. // machine_exists(machine)
  45. // Returns the index to the method who's name matches methodName with case insensitivity in machine, or -1 if it does not exist in machine.
  46. int machine_findMethod(const MediaMachine& machine, const ReadableString& methodName);
  47. // Assign an input argument of a method before your call to machine_executeMethod.
  48. // Pre-condition:
  49. // * The machine must exist.
  50. // machine_exists(machine)
  51. // * methodIndex must refer to the method to call in machine.
  52. // 0 <= methodIndex < machine_getMethodCount(machine)
  53. // * inputIndex must refer to an input of the method in machine.
  54. // 0 <= inputIndex < machine_getInputCount(machine, methodIndex)
  55. // Side-effect: Sets the input at inputIndex in machine's method at methodIndex to input.
  56. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, int32_t input);
  57. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, const FixedPoint& input);
  58. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, const AlignedImageU8& input);
  59. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, const OrderedImageRgbaU8& input);
  60. // Call a method in the media machine, reading from input registers and writing to output registers.
  61. // Pre-condition:
  62. // * The machine must exist.
  63. // machine_exists(machine)
  64. // * All inputs of the method must be assigned before the call using the same methodIndex.
  65. // * methodIndex must refer to the method to call in machine.
  66. // 0 <= methodIndex < machine_getMethodCount(machine)
  67. // Side-effect: Writes the results to output arguments.
  68. void machine_executeMethod(MediaMachine& machine, int methodIndex);
  69. // Read output register at outputIndex
  70. // Pre-condition:
  71. // * The machine must exist.
  72. // machine_exists(machine)
  73. // * methodIndex must refer to the method in machine's last call to machine_executeMethod.
  74. // 0 <= methodIndex < machine_getMethodCount(machine)
  75. // * The output index must be within range.
  76. // 0 <= outputIndex < machine_getOutputCount(machine, methodIndex)
  77. // Post-condition: Returns the output at outputIndex.
  78. FixedPoint machine_getFixedPointOutputByIndex(const MediaMachine& machine, int methodIndex, int outputIndex);
  79. AlignedImageU8 machine_getImageU8OutputByIndex(const MediaMachine& machine, int methodIndex, int outputIndex);
  80. OrderedImageRgbaU8 machine_getImageRgbaU8OutputByIndex(const MediaMachine& machine, int methodIndex, int outputIndex);
  81. // Pre-condition:
  82. // * The machine must exist.
  83. // machine_exists(machine)
  84. // Post-condition: Returns the number of methods in machine.
  85. int machine_getMethodCount(const MediaMachine& machine);
  86. // Pre-condition:
  87. // * The machine must exist.
  88. // machine_exists(machine)
  89. // * methodIndex must refer to the method that you want to get the name from.
  90. // 0 <= methodIndex < machine_getMethodCount(machine)
  91. // Post-condition: Returns the name of the method at methodIndex in machine.
  92. String machine_getMethodName(const MediaMachine& machine, int methodIndex);
  93. // Pre-condition:
  94. // * The machine must exist.
  95. // machine_exists(machine)
  96. // * methodIndex must refer to the method that you want to get the input count from.
  97. // 0 <= methodIndex < machine_getMethodCount(machine)
  98. // Post-condition: Returns the input argument count for the method at methodIndex in machine.
  99. int machine_getInputCount(const MediaMachine& machine, int methodIndex);
  100. // Pre-condition:
  101. // * The machine must exist.
  102. // machine_exists(machine)
  103. // * methodIndex must refer to the method that you want to get the output count from.
  104. // 0 <= methodIndex < machine_getMethodCount(machine)
  105. // Post-condition: Returns the output argument count for the method at methodIndex in machine.
  106. int machine_getOutputCount(const MediaMachine& machine, int methodIndex);
  107. // Pre-condition:
  108. // * The machine must exist.
  109. // machine_exists(machine)
  110. // * methodIndex must refer to the method that you want to get an input argument's name from.
  111. // 0 <= methodIndex < machine_getMethodCount(machine)
  112. // * The inputIndex must refer to the input argument that you want to get the name of.
  113. // 0 <= inputIndex < machine_getInputCount(machine, methodIndex)
  114. // Post-condition: Returns the input argument name at inputIndex for the method at methodIndex in machine.
  115. String machine_getInputName(const MediaMachine& machine, int methodIndex, int inputIndex);
  116. // Pre-condition:
  117. // * The machine must exist.
  118. // machine_exists(machine)
  119. // * methodIndex must refer to the method that you want to get an output argument's name from.
  120. // 0 <= methodIndex < machine_getMethodCount(machine)
  121. // * The outputIndex must refer to the output argument that you want to get the name of.
  122. // 0 <= outputIndex < machine_getOutputCount(machine, methodIndex)
  123. // Post-condition: Returns the output argument name at outputIndex for the method at methodIndex in machine.
  124. String machine_getOutputName(const MediaMachine& machine, int methodIndex, int outputIndex);
  125. // Helper function counting the number of arguments given to it.
  126. inline constexpr int machine_argCount() {
  127. return 0;
  128. }
  129. template<typename HEAD, typename... TAIL>
  130. inline constexpr int machine_argCount(HEAD& first, TAIL&... args) {
  131. return machine_argCount(args...) + 1;
  132. }
  133. // A temporary type generated from () calls to MediaMethod, which is used for writing outputs to targets within the next ().
  134. class MediaResult {
  135. private:
  136. // Holding the machine by reference prevents storing MediaResult,
  137. // because it may not be used after other calls to the machine.
  138. // It is only used to assign outputs with the () operator.
  139. MediaMachine &machine;
  140. int methodIndex;
  141. void writeResult(int outputIndex, int8_t& target) {
  142. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  143. }
  144. void writeResult(int outputIndex, int16_t& target) {
  145. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  146. }
  147. void writeResult(int outputIndex, int32_t& target) {
  148. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  149. }
  150. void writeResult(int outputIndex, int64_t& target) {
  151. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  152. }
  153. void writeResult(int outputIndex, FixedPoint& target) {
  154. target = machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex);
  155. }
  156. void writeResult(int outputIndex, AlignedImageU8& target) {
  157. target = machine_getImageU8OutputByIndex(this->machine, this->methodIndex, outputIndex);
  158. }
  159. void writeResult(int outputIndex, OrderedImageRgbaU8& target) {
  160. target = machine_getImageRgbaU8OutputByIndex(this->machine, this->methodIndex, outputIndex);
  161. }
  162. inline void writeResults(int firstInputIndex) {}
  163. template<typename HEAD, typename... TAIL>
  164. inline void writeResults(int firstInputIndex, HEAD& first, TAIL&... args) {
  165. this->writeResult(firstInputIndex, first);
  166. this->writeResults(firstInputIndex + 1, args...);
  167. }
  168. public:
  169. MediaResult(MediaMachine& machine, int methodIndex)
  170. : machine(machine), methodIndex(methodIndex) {}
  171. // Write target references within () after a call to assign multiple outputs
  172. template <typename... ARGS>
  173. void operator () (ARGS&... args) {
  174. int givenCount = machine_argCount(args...);
  175. int expectedCount = machine_getOutputCount(this->machine, this->methodIndex);
  176. if (givenCount != expectedCount) {
  177. throwError("The call to ", machine_getMethodName(this->machine, this->methodIndex), " expected ", expectedCount, " outputs, but ", givenCount, " references were assigned.\n");
  178. }
  179. this->writeResults(0, args...);
  180. }
  181. };
  182. // How to call a MediaMethod:
  183. // * Using arguments in the same order as declared in the Media Machine Code.
  184. // myMediaMethod(inputA, inputB, inputC...)(outputX, outputY...)
  185. // This allow returning more than one return value without having to declare any structures in the virtual machine.
  186. // * Using keyword arguments.
  187. // You can also call myMediaMethod and just define a lambda that is called with the argument name and index for each input argument.
  188. // Then your function calls machine_setInputByIndex with the given arguments and the input data identified by name.
  189. class MediaMethod {
  190. public:
  191. MediaMachine machine;
  192. int methodIndex; // Index of the method being called.
  193. int contextIndex; // Index used to know from which context implicit variables are being fetched.
  194. private:
  195. inline void setInputs(int firstInputIndex) {}
  196. template<typename HEAD, typename... TAIL>
  197. inline void setInputs(int firstInputIndex, const HEAD &first, TAIL&&... args) {
  198. machine_setInputByIndex(this->machine, this->methodIndex, firstInputIndex, first);
  199. this->setInputs(firstInputIndex + 1, args...);
  200. }
  201. public:
  202. MediaMethod()
  203. : methodIndex(-1), contextIndex(0) {}
  204. MediaMethod(const MediaMachine& machine, int methodIndex, int contextIndex)
  205. : machine(machine), methodIndex(methodIndex), contextIndex(contextIndex) {}
  206. // MediaMethod can be called like a function using arguments, returning MediaResult for assigning outputs by reference.
  207. // Useful when you know the arguments in advance.
  208. template <typename... ARGS>
  209. MediaResult operator () (ARGS&&... args) {
  210. int givenCount = machine_argCount(args...);
  211. int expectedCount = machine_getInputCount(this->machine, this->methodIndex);
  212. if (givenCount != expectedCount) {
  213. throwError("The call to ", machine_getMethodName(this->machine, this->methodIndex), " expected ", expectedCount, " inputs, but ", givenCount, " values were given.\n");
  214. }
  215. this->setInputs(0, args...);
  216. machine_executeMethod(this->machine, this->methodIndex);
  217. return MediaResult(this->machine, this->methodIndex);
  218. }
  219. // MediaMethod can also take the inputs as keyword arguments by getting a callback with the index and name of each input to assign.
  220. // The function setInputAction should simply make a call to machine_setInputByIndex with the provided machine, methodIndex, inputIndex and the value corresponding to argumentName in setInputAction.
  221. // If you don't recognize argumentName, then throw an exception because default input arguments are currently not implemented.
  222. // Useful when the called function can be extended or reduced with only the arguments needed.
  223. MediaResult callUsingKeywords(std::function<void(MediaMachine &machine, int methodIndex, int inputIndex, const ReadableString &argumentName)> setInputAction);
  224. };
  225. // Post-condition: Returns a MediaMethod structure, which can be stored as a reference counting function pointer that keeps the virtual machine alive.
  226. MediaMethod machine_getMethod(MediaMachine& machine, const ReadableString& methodName, int contextIndex, bool mustExist = true);
  227. }
  228. #endif