Shade: Conversions
Previously, I described the type system Shade uses for connections between shader fragments. Today I’m going to discuss the process by which conversions are searched for.
An individual conversion in Shade is a simple thing, essentially a conversion is a highly-specialized shader feature. A conversion has semantic types for input and output, as well as a snippet of code code that will perform the conversion. Shade’s conversion search routine takes a source and target parameter, and returns a list of conversions and an integer representing the “difficulty” of the conversion. Difficulty is a heuristic. The only values with a concrete meaning are 0 (the types match exactly) and int.MaxValue (the conversion is impossible). Beyond that, difficulty is only useful as a relative measure — lower, of course, being better.
In order for a conversion to be possible at all, the source type must contain at least all of the trait names as the target type (but the values can, of course, be different). Conversions may not add new traits to a type, they can only change or remove them. So, for example, it is possible to convert from type:float4,basis:model to type:float4,basis:view. It is possible to convert from type:float3,use:normal,basis:clip to type:float3,basis:world (discarding the use trait in the process). However, it is not possible to convert from type:float3 to type:float2,basis:view (without information about the basis the vector originates in, there’s no sane way to bring it into a defined basis). This relationship is referred to as the compatibility of the two types, and it is not a transitive relationship. It’s also not a boolean relationship - types that have more traits in common are more compatible (type:float4,basis:world is more compatible with type:float4,basis:clip than it is with just type:float4. type:float4, however, is not compatible at all with type:float4,basis:world).
The traits that exist on both the source and the target, but whose values differ, are known as the “candidate traits.” The psuedo-type that is defined by the candidate traits is checked against every conversion available to Shade. Any conversion with an input that is compatible with the source type is a potential addition to the final result set. For every potential conversion found, a temporary type is created that represents the type of the original source parameter after application of the conversion, and the conversion search routine is repeated recursively using this new parameter as the source. The stopping case is when the source and target parameters are the same (success) or no acceptable conversions exist (failure). The child conversion with the lowest difficulty is selected as the result, and returned.
One thorny issue is that conversions discard any trait the source has that the target lacks. This is undesirable, because it means that the deeper the graph, the more likely you are to lose a lot of the useful semantic information that improves the stability and utility of the tool. One direction I plan on exploring in the future is modifying the conversion search so that such traits are simply propagated through unchanged, to prevent this problem from occuring.
