ravk (pronounced "Ravick") is a command-line interface that reports useful facts about a Vulkan integration in terms of a vk.xml specification.
You can use ravk to generate modules that operate independently from this collection, generate Racket code fragments for auditing purposes, and to review information about the Vulkan specification itself.
Many commands operate on a copy of vk.xml, and you can specify which to use.
$ ravk ... # Use local mirror of vk.xml
$ ravk --latest ... # (or -l) Use latest vk.xml from the Khronos Group.
Use ravk show to view helpful data about the code that will run on the host system in an integration.
It’s critically important that all concerned parties keep an eye on the Vulkan specification. You will need information about the spec to know if you should upgrade this collection or use a different version of vk.xml to generate code.
$ ravk show spec # prints local mirror of vk.xml for offline use.
$ ravk show spec --xexpr # (or -x) prints vk.xml as an X-expression. Ignored if -v is set.
$ ravk show spec --version # (or -v) Show specification version.
$ ravk -l show spec -x # X-expression of latest vk.xml
$ ravk generate heading.rkt body.rkt footer.rkt ...
The generate command prints code to STDOUT. The output may be a full Racket module body complete with a #lang line, or a parseable fragment that follows certain assumptions.
Use this command to inspect the Vulkan registry with custom views, or to prepare code with an adjustable level of overhead.
The arguments are paths to Racket modules that must provide a procedure called in-fragment. It returns a sequence of Racket code given a Vulkan specification and a shared configuration.
(require racket/generator) (define (in-fragment registry [config #hash()]) (in-generator (yield "#lang racket/base") (yield '(define something 1))))
If the first element of the sequence is a string, it will be printed in display mode. This is to support #lang lines. In this case, a trailing newline character is optional. In all other cases, each element of the sequence is a datum that, when printed in write mode, will appear in a file as a valid Racket expression.
config is populated by the ravk generate command such that the long form name of each option is a key in the dictionary (e.g. --enable-auto-check-vkresult has the key 'enable-auto-check-vkresult. Keys might not be present, and the value of each key depends on the option. See Generator Configuration Space.
The output of each module appears follows the order declared in the command line. There is no guarentee that the output will be a valid program, so you have to know what you are asking for.
Besides paths to Racket modules, you can symbolically refer to built-in generators. In addition, the output of ravk generate unsafe in particular is the content of vulkan/unsafe.
This effectively locks down a copy of Racket code against a Vulkan spec version, and makes it possible for some packages to operate without a dependency on this collection, and with minimal overhead.
$ ravk generate unsafe > unsafe.rkt
Here are the currently supported built-in generators:
FFI bindings for Vulkan across all platforms and extensions for the given specification.
A code generator module lazily produces Racket code in terms of a Vulkan specification. To write one, you need to know the Vulkan Registry.
It so happens that code generator modules are easy to write with Racket Generators. Be careful not to confuse the two.
In this example, we generate a Racket module that prints platform-specific type names when instantiated. registry-xexpr is bound to an X-expression form of the <registry> (root) element of a Vulkan specification.
#lang racket/base (provide in-fragment) (require racket/generator racket/string txexpr) (define (in-fragment registry-xexpr [config #hash()]) (in-generator (yield "#lang racket/base") (define platform-type-elements (findf*-txexpr registry-xexpr (λ (x) (and (list? x) (string-suffix? (attr-ref x 'requires "") ".h")))) (for ([element platform-type-elements]) (yield `(displayln ,(attr-ref element 'name)))))))
For Vulkan 1.1.126, this session holds:
$ ravk generate gen-platform-type-printer.rkt > print-platform-types.rkt
$ racket print-platform-types.rkt
Code generators can also generate documentation or report data. The possibilities are exciting, but some words of warning:
A search through the registry element is expensive. Use memoization and limit your search space whenever possible.
The Vulkan specification is machine-readable, but human-comprehensible. Expect to write weird and wonderful things to make your program work reliably. Mostly weird.
There are no guarentees that the order of data you encounter is the order it should appear in Racket.
The built-in code generators share configuration that controls their output. You can control this configuration using command-line flags.
The following values are booleans for in-fragment’s purposes. If the flag is not set, the key will not be set in the dictionary passed to in-fragment.
--enable-auto-check-vkresult: When set, all foreign function calls that return a VkResult will be automatically checked in a wrapping Racket procedure. If the code is an error code, the wrapping procedure will raise exn:fail.
--enable-symbolic-enums: When set, all C enum types are represented using either _enum or _bitmask depending on their intended use. You must then use symbols to represent enumerants according to the rules of _enum or _bitmask in your program.