Created
March 6, 2016 00:23
-
-
Save nsf/b094984d1cbd702dc7c9 to your computer and use it in GitHub Desktop.
VkCpp in Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"strconv" | |
"strings" | |
) | |
type AnalyzedType struct { | |
Name string | |
Type string | |
Extra string | |
// result of Analyze | |
IsConst bool | |
IsPointer bool | |
IsBlank bool | |
IsArray bool | |
Arity int | |
Prefix string | |
Suffix string | |
} | |
func NewAnalyzedType(name, typ, extra string) AnalyzedType { | |
at := AnalyzedType{ | |
Name: name, | |
Type: typ, | |
Extra: strings.TrimSpace(extra), | |
} | |
at.Analyze() | |
return at | |
} | |
func (at *AnalyzedType) Analyze() { | |
if at.Extra == "" { | |
at.IsBlank = true | |
return | |
} | |
if strings.HasPrefix(at.Extra, "const ") { | |
at.IsConst = true | |
} | |
if strings.HasSuffix(at.Extra, "]") { | |
e := at.Extra | |
i := strings.LastIndex(e, "[") | |
if i == -1 { | |
panic("invalid type") | |
} | |
at.Arity, _ = strconv.Atoi(e[i+1 : len(e)-1]) | |
at.IsArray = true | |
} | |
if strings.ContainsAny(at.Extra, "*]") { | |
at.IsPointer = true | |
} | |
at.AssemblePrefixSufix() | |
} | |
func (at *AnalyzedType) AssemblePrefixSufix() { | |
e := at.Extra | |
if strings.HasPrefix(e, "const ") { | |
e = strings.TrimPrefix(e, "const ") | |
at.Prefix = "const " | |
} | |
if strings.Contains(e, "[") { | |
// there are better ways to replace [] or [2] with *, but let's keep | |
// this one for now | |
beg := strings.Index(e, "[") | |
end := strings.Index(e, "]") | |
if beg == -1 || end == -1 { | |
panic("invalid type") | |
} | |
e = e[:beg] + "*" + e[end+1:] | |
} | |
at.Suffix = e | |
} | |
type TypeConverter interface { | |
CppToVkArg(at AnalyzedType, src string) string | |
CppToVk(at AnalyzedType, src, dst string) string | |
VkToCpp(at AnalyzedType, src string) string | |
} | |
type CommonConverter struct { | |
CppName string | |
VkName string | |
} | |
type NopConverter struct{} | |
func (NopConverter) CppToVkArg(at AnalyzedType, src string) string { | |
return src | |
} | |
func (NopConverter) CppToVk(at AnalyzedType, src, dst string) string { | |
return fmt.Sprintf("%s = %s;", dst, src) | |
} | |
func (NopConverter) VkToCpp(at AnalyzedType, src string) string { | |
return fmt.Sprintf("return %s;", src) | |
} | |
type BitMaskConverter CommonConverter | |
func (c *BitMaskConverter) CppToVkArg(at AnalyzedType, src string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).CppToVkArg(at, src) | |
} | |
return fmt.Sprintf("static_cast<%s>(%s)", c.VkName, src) | |
} | |
func (c *BitMaskConverter) CppToVk(at AnalyzedType, src, dst string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).CppToVk(at, src, dst) | |
} | |
return fmt.Sprintf("%s = static_cast<%s>(%s);", dst, c.VkName, src) | |
} | |
func (c *BitMaskConverter) VkToCpp(at AnalyzedType, src string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).VkToCpp(at, src) | |
} | |
return fmt.Sprintf("return %s(%s);", c.CppName, src) | |
} | |
type StaticCastConverter CommonConverter | |
func (c *StaticCastConverter) CppToVkArg(at AnalyzedType, src string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).CppToVkArg(at, src) | |
} | |
return fmt.Sprintf("static_cast<%s>(%s)", c.VkName, src) | |
} | |
func (c *StaticCastConverter) CppToVk(at AnalyzedType, src, dst string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).CppToVk(at, src, dst) | |
} | |
return fmt.Sprintf("%s = static_cast<%s>(%s);", dst, c.VkName, src) | |
} | |
func (c *StaticCastConverter) VkToCpp(at AnalyzedType, src string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).VkToCpp(at, src) | |
} | |
return fmt.Sprintf("return static_cast<%s>(%s);", c.CppName, src) | |
} | |
type ArrayConverter CommonConverter | |
func (c *ArrayConverter) CppToVkArg(at AnalyzedType, src string) string { | |
return src | |
} | |
func (c *ArrayConverter) CppToVk(at AnalyzedType, src, dst string) string { | |
return fmt.Sprintf("std::memcpy(%s, %s, %d * sizeof(%s));", dst, src, at.Arity, c.VkName) | |
} | |
func (c *ArrayConverter) VkToCpp(at AnalyzedType, src string) string { | |
return fmt.Sprintf("return reinterpret_cast<const %s*>(%s);", c.CppName, src) | |
} | |
type ReinterpretCastConverter CommonConverter | |
func (c *ReinterpretCastConverter) CppToVkArg(at AnalyzedType, src string) string { | |
if at.IsBlank { | |
return (*StaticCastConverter)(c).CppToVkArg(at, src) | |
} | |
return fmt.Sprintf("reinterpret_cast<%s%s%s>(%s)", at.Prefix, c.VkName, at.Suffix, src) | |
} | |
func (c *ReinterpretCastConverter) CppToVk(at AnalyzedType, src, dst string) string { | |
if at.IsBlank { | |
return (*StaticCastConverter)(c).CppToVk(at, src, dst) | |
} | |
return fmt.Sprintf("%s = reinterpret_cast<%s%s%s>(%s);", dst, at.Prefix, c.VkName, at.Suffix, src) | |
} | |
func (c *ReinterpretCastConverter) VkToCpp(at AnalyzedType, src string) string { | |
if at.IsBlank { | |
return (*StaticCastConverter)(c).VkToCpp(at, src) | |
} | |
return fmt.Sprintf("return reinterpret_cast<%s%s%s>(%s);", at.Prefix, c.CppName, at.Suffix, src) | |
} | |
type HandleConverter CommonConverter | |
func (c *HandleConverter) CppToVkArg(at AnalyzedType, src string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).CppToVkArg(at, src) | |
} | |
return fmt.Sprintf("static_cast<%s>(%s)", c.VkName, src) | |
} | |
func (c *HandleConverter) CppToVk(at AnalyzedType, src, dst string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).CppToVk(at, src, dst) | |
} | |
return fmt.Sprintf("%s = static_cast<%s>(%s);", dst, c.VkName, src) | |
} | |
func (c *HandleConverter) VkToCpp(at AnalyzedType, src string) string { | |
if at.IsPointer { | |
return (*ReinterpretCastConverter)(c).VkToCpp(at, src) | |
} | |
return fmt.Sprintf("return %s(%s);", c.CppName, src) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/xml" | |
"flag" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"os" | |
"sort" | |
"strings" | |
"unicode" | |
) | |
const helpText = ` | |
usage: vk_cpp_generator <spec_file> [-o <output_file>] | |
Convert XML specification into C++ header. Writes to STDOUT, unless | |
<output_file> is specified. | |
Options: | |
` | |
var outputFile = flag.String("o", "", "Write output to file instead of STDOUT") | |
func panicIfError(err error) { | |
if err != nil { | |
panic(err) | |
} | |
} | |
func convertEnumValueName(expand, enum, name string) string { | |
// strip prefix | |
if expand != "" && strings.HasPrefix(name, expand) { | |
name = strings.TrimPrefix(name, expand) | |
} else { | |
senum, _ := trimTagSuffix(enum) | |
senum = toSnakeCase(strings.TrimSuffix(senum, "FlagBits")) | |
if strings.HasPrefix(name, senum) { | |
name = name[len(senum)+1:] | |
} | |
} | |
if enum == "VkResult" { | |
// special case | |
name = strings.TrimPrefix(name, "VK_") | |
} | |
name, _ = trimTagSuffix(name) | |
name = strings.TrimSuffix(name, "_BIT") | |
name = toCamelCase(name) | |
return "e" + name | |
} | |
func convertVkName(name string) string { | |
return strings.TrimPrefix(name, "Vk") | |
} | |
func convertEnumName(name string) string { return convertVkName(name) } | |
func convertHandleName(name string) string { return convertVkName(name) } | |
func convertBitMaskName(name string) string { return convertVkName(name) } | |
func convertStructName(name string) string { return convertVkName(name) } | |
func convertCommandName(name string) string { | |
name = strings.TrimPrefix(name, "vk") | |
return strings.ToLower(name[0:1]) + name[1:] | |
} | |
// PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO -> PipelineDepthStencilStateCreateInfo | |
func toCamelCase(s string) string { | |
if len(s) <= 1 { | |
return s | |
} | |
var b bytes.Buffer | |
var prev rune | |
for i, r := range s { | |
if r != '_' { | |
if i == 0 || prev == '_' || unicode.IsDigit(prev) { | |
b.WriteRune(r) | |
} else { | |
b.WriteRune(unicode.ToLower(r)) | |
} | |
} | |
prev = r | |
} | |
return b.String() | |
} | |
// PipelineDepthStencilStateCreateInfo -> PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO | |
func toSnakeCase(s string) string { | |
if len(s) <= 1 { | |
return s | |
} | |
var b bytes.Buffer | |
var prev rune | |
for i, r := range s { | |
if i != 0 && unicode.IsUpper(r) && (unicode.IsLower(prev) || unicode.IsDigit(prev)) { | |
b.WriteRune('_') | |
} | |
b.WriteRune(unicode.ToUpper(r)) | |
prev = r | |
} | |
return b.String() | |
} | |
var knownTags = []string{ | |
"KHR", | |
"EXT", | |
} | |
func trimTagSuffix(s string) (string, string) { | |
for _, tag := range knownTags { | |
stag := "_" + tag | |
if strings.HasSuffix(s, stag) { | |
return strings.TrimSuffix(s, stag), tag | |
} | |
if strings.HasSuffix(s, tag) { | |
return strings.TrimSuffix(s, tag), tag | |
} | |
} | |
return s, "" | |
} | |
func bitMaskNameToEnumName(s string) string { | |
s, tagUsed := trimTagSuffix(s) | |
if strings.HasSuffix(s, "Flags") { | |
s = strings.TrimSuffix(s, "Flags") + "FlagBits" | |
} | |
return s + tagUsed | |
} | |
func structToTypeName(s string) string { | |
return "VK_STRUCTURE_TYPE_" + toSnakeCase(s) | |
} | |
func nameExtraArrayFix(name, extra *string) { | |
// hack to fix the vk.xml, some names end with [2], let's move it to extra | |
if strings.HasSuffix(*name, "[2]") { | |
*name = strings.TrimSuffix(*name, "[2]") | |
*extra += "[2]" | |
} | |
} | |
type xmlRegistry struct { | |
XMLName string `xml:"registry"` | |
Types struct { | |
Type []xmlType `xml:"type"` | |
} `xml:"types"` | |
Enums []xmlEnums `xml:"enums"` | |
Commands struct { | |
Command []xmlCommand `xml:"command"` | |
} `xml:"commands"` | |
Extensions struct { | |
Extension []xmlExtension `xml:"extension"` | |
} `xml:"extensions"` | |
} | |
type xmlExtension struct { | |
Protect string `xml:"protect,attr"` | |
Require struct { | |
Types []struct { | |
Name string `xml:"name,attr"` | |
} `xml:"type"` | |
Commands []struct { | |
Name string `xml:"name,attr"` | |
} `xml:"command"` | |
} `xml:"require"` | |
} | |
type xmlCommand struct { | |
Proto xmlTypeName `xml:"proto"` | |
Params []xmlTypeName `xml:"param"` | |
} | |
type xmlType struct { | |
Name string `xml:"name,attr"` | |
Requires string `xml:"requires,attr"` | |
Category string `xml:"category,attr"` | |
ReturnedOnly bool `xml:"returnedonly,attr"` | |
Members []xmlTypeName `xml:"member"` | |
InnerName string `xml:"name"` | |
InnerType string `xml:"type"` | |
} | |
type xmlTypeName struct { | |
Type string `xml:"type"` | |
Name string `xml:"name"` | |
Extra string `xml:",chardata"` | |
} | |
type xmlEnums struct { | |
Name string `xml:"name,attr"` | |
Type string `xml:"type,attr"` | |
Expand string `xml:"expand,attr"` | |
Values []xmlEnum `xml:"enum"` | |
} | |
type xmlEnum struct { | |
Name string `xml:"name,attr"` | |
Value int `xml:"value,attr"` | |
BitPos int `xml:"bitpos,attr"` | |
} | |
type HeaderParams struct { | |
GuardBegin string | |
GuardEnd string | |
Namespace string | |
} | |
type Handle struct { | |
Name string | |
VkName string | |
TypeSafe bool | |
} | |
type EnumValue struct { | |
Name string | |
VkName string | |
} | |
type Protect struct { | |
Begin string | |
End string | |
} | |
type Enum struct { | |
Protect Protect | |
Name string | |
Values []EnumValue | |
used bool | |
} | |
type BitMask struct { | |
Protect Protect | |
Name string | |
VkName string | |
Enum *Enum | |
} | |
type Command struct { | |
Protect Protect | |
Name string | |
VkName string | |
RetType string | |
RetVkType string | |
Parameters []CommandParameter | |
} | |
type CommandParameter struct { | |
Name string | |
Type string | |
VkType string | |
AnalyzedType AnalyzedType | |
Converter TypeConverter | |
} | |
type Struct struct { | |
Protect Protect | |
Name string | |
VkName string | |
TypeName string | |
HasSType bool | |
Members []StructMember | |
ReadOnly bool | |
} | |
type StructMember struct { | |
Name string | |
Type string | |
VkType string | |
AnalyzedType AnalyzedType | |
Converter TypeConverter | |
} | |
type Context struct { | |
Handles []Handle | |
BitMasks []BitMask | |
Enums []Enum | |
Structs []Struct | |
Commands []Command | |
converters map[string]TypeConverter | |
} | |
type StructsSort []Struct | |
func (s StructsSort) Len() int { return len(s) } | |
func (s StructsSort) Less(i, j int) bool { return s[i].VkName < s[j].VkName } | |
func (s StructsSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |
func (ctx *Context) SortStructsByDeps() { | |
// we need to sort struct by deps | |
set := map[string]*Struct{} | |
for i, s := range ctx.Structs { | |
set[s.VkName] = &ctx.Structs[i] | |
} | |
// now we just go over structs many times, if a struct has no deps in set, | |
// we remove it from set and add it to array, then repeat, note that this | |
// process will not break cycles, but I've added protection against cycles | |
lastOutLen := 0 | |
out := make([]Struct, 0, len(ctx.Structs)) | |
for len(set) > 0 { | |
for _, s := range set { | |
hasDeps := false | |
for _, m := range s.Members { | |
if _, ok := set[m.AnalyzedType.Type]; ok { | |
hasDeps = true | |
break | |
} | |
} | |
if !hasDeps { | |
out = append(out, *s) | |
} | |
} | |
if len(out) == lastOutLen { | |
panic("circular dependencies detected") | |
} | |
for i := lastOutLen; i < len(out); i++ { | |
delete(set, out[i].VkName) | |
} | |
sort.Sort(StructsSort(out[lastOutLen:])) | |
lastOutLen = len(out) | |
} | |
ctx.Structs = out | |
} | |
func (ctx *Context) ResolveStructMemberConverters() { | |
for _, s := range ctx.Structs { | |
for i := range s.Members { | |
m := &s.Members[i] | |
if m.AnalyzedType.IsArray { | |
// hacky way to handle array types | |
m.Converter = &ArrayConverter{ | |
VkName: m.AnalyzedType.Type, | |
CppName: convertVkName(m.AnalyzedType.Type), | |
} | |
continue | |
} | |
// try to get a type-specific converter | |
conv, ok := ctx.converters[m.AnalyzedType.Type] | |
if ok { | |
m.Converter = conv | |
} | |
} | |
} | |
} | |
func (ctx *Context) ResolveCommandParameterConverters() { | |
for _, c := range ctx.Commands { | |
for i := range c.Parameters { | |
p := &c.Parameters[i] | |
conv, ok := ctx.converters[p.AnalyzedType.Type] | |
if ok { | |
p.Converter = conv | |
} | |
} | |
} | |
} | |
func assembleType(typ, extra string) string { | |
extra = strings.TrimSpace(extra) | |
out := typ | |
if strings.HasPrefix(extra, "const ") { | |
out = "const " + out | |
extra = strings.TrimSpace(strings.TrimPrefix(extra, "const ")) | |
} | |
if len(extra) > 0 && extra[0] == '[' && extra[len(extra)-1] == ']' { | |
extra = "*" | |
} | |
return out + extra | |
} | |
func newContext(registry *xmlRegistry) Context { | |
var ctx Context | |
ctx.converters = map[string]TypeConverter{} | |
enumMap := map[string]*Enum{} // vk enum name -> Enum | |
protectMap := map[string]Protect{} // vk type name -> protect string | |
for _, e := range registry.Extensions.Extension { | |
if e.Protect == "" { | |
continue | |
} | |
for _, t := range e.Require.Types { | |
protectMap[t.Name] = Protect{ | |
Begin: "#ifdef " + e.Protect, | |
End: "#endif", | |
} | |
} | |
for _, c := range e.Require.Commands { | |
protectMap[c.Name] = Protect{ | |
Begin: "#ifdef " + e.Protect, | |
End: "#endif", | |
} | |
} | |
} | |
for _, xe := range registry.Enums { | |
e := &Enum{ | |
Protect: protectMap[xe.Name], | |
Name: convertEnumName(xe.Name), | |
} | |
for _, v := range xe.Values { | |
e.Values = append(e.Values, EnumValue{ | |
Name: convertEnumValueName(xe.Expand, xe.Name, v.Name), | |
VkName: v.Name, | |
}) | |
} | |
enumMap[xe.Name] = e | |
ctx.converters[xe.Name] = &StaticCastConverter{ | |
CppName: e.Name, | |
VkName: xe.Name, | |
} | |
} | |
// Separate pass on bitmasks, so that we know which enums are used. | |
// Technically bitmasks are placed before enums in vk.xml, but who | |
// guaranees that. | |
for _, t := range registry.Types.Type { | |
switch t.Category { | |
case "bitmask": | |
if t.InnerType != "VkFlags" { | |
log.Printf("unrecognized bitmask type: %s", t.InnerType) | |
continue | |
} | |
enumName := bitMaskNameToEnumName(t.InnerName) | |
enum, ok := enumMap[enumName] | |
if !ok { | |
// broken xml, some enums are missing, let's just create them | |
enum = &Enum{Name: convertEnumName(enumName)} | |
enumMap[enumName] = enum | |
} | |
// we also clear protect, because in all cases bit mask is already | |
// wrapped | |
enum.Protect = Protect{} | |
enum.used = true | |
bm := BitMask{ | |
Protect: protectMap[t.InnerName], | |
Name: convertBitMaskName(t.InnerName), | |
VkName: t.InnerName, | |
Enum: enum, | |
} | |
ctx.BitMasks = append(ctx.BitMasks, bm) | |
ctx.converters[t.InnerName] = &BitMaskConverter{ | |
CppName: bm.Name, | |
VkName: bm.VkName, | |
} | |
} | |
} | |
for _, t := range registry.Types.Type { | |
switch t.Category { | |
case "handle": | |
h := Handle{ | |
Name: convertHandleName(t.InnerName), | |
VkName: t.InnerName, | |
TypeSafe: t.InnerType == "VK_DEFINE_HANDLE", | |
} | |
ctx.Handles = append(ctx.Handles, h) | |
ctx.converters[t.InnerName] = &HandleConverter{ | |
CppName: h.Name, | |
VkName: h.VkName, | |
} | |
case "enum": | |
enum, ok := enumMap[t.Name] | |
if !ok { | |
enum = &Enum{Name: convertEnumName(t.Name)} | |
} | |
if enum.used { | |
continue | |
} | |
ctx.Enums = append(ctx.Enums, *enum) | |
case "struct", "union": | |
if t.Name == "VkRect3D" { // TODO: vulkan/vulkan.h contains no such thing | |
continue | |
} | |
name := convertStructName(t.Name) | |
s := Struct{ | |
Protect: protectMap[t.Name], | |
Name: name, | |
VkName: t.Name, | |
TypeName: structToTypeName(name), | |
ReadOnly: t.ReturnedOnly, | |
} | |
for _, m := range t.Members { | |
if m.Name == "sType" { | |
s.HasSType = true | |
} | |
nameExtraArrayFix(&m.Name, &m.Extra) | |
s.Members = append(s.Members, StructMember{ | |
Name: m.Name, | |
Type: assembleType(convertVkName(m.Type), m.Extra), | |
VkType: assembleType(m.Type, m.Extra), | |
AnalyzedType: NewAnalyzedType(m.Name, m.Type, m.Extra), | |
Converter: NopConverter{}, | |
}) | |
} | |
ctx.Structs = append(ctx.Structs, s) | |
ctx.converters[t.Name] = &ReinterpretCastConverter{ | |
CppName: s.Name, | |
VkName: s.VkName, | |
} | |
} | |
} | |
for _, c := range registry.Commands.Command { | |
cmd := Command{ | |
Protect: protectMap[c.Proto.Name], | |
Name: convertCommandName(c.Proto.Name), | |
VkName: c.Proto.Name, | |
RetType: assembleType(convertVkName(c.Proto.Type), c.Proto.Extra), | |
RetVkType: assembleType(c.Proto.Type, c.Proto.Extra), | |
} | |
for _, p := range c.Params { | |
cp := CommandParameter{ | |
Name: p.Name, | |
Type: assembleType(convertVkName(p.Type), p.Extra), | |
VkType: assembleType(p.Type, p.Extra), | |
AnalyzedType: NewAnalyzedType(p.Name, p.Type, p.Extra), | |
Converter: NopConverter{}, | |
} | |
cmd.Parameters = append(cmd.Parameters, cp) | |
} | |
ctx.Commands = append(ctx.Commands, cmd) | |
} | |
ctx.SortStructsByDeps() | |
ctx.ResolveStructMemberConverters() | |
ctx.ResolveCommandParameterConverters() | |
return ctx | |
} | |
func main() { | |
flag.Usage = func() { | |
fmt.Printf(helpText[1:]) | |
flag.PrintDefaults() | |
} | |
flag.Parse() | |
nargs := flag.NArg() | |
if nargs != 1 { | |
flag.Usage() | |
os.Exit(1) | |
} | |
var output io.Writer | |
if *outputFile != "" { | |
f, err := os.Create(*outputFile) | |
panicIfError(err) | |
defer f.Close() | |
output = f | |
} else { | |
output = os.Stdout | |
} | |
specfile := flag.Arg(0) | |
specxml, err := ioutil.ReadFile(specfile) | |
panicIfError(err) | |
var registry xmlRegistry | |
panicIfError(xml.Unmarshal(specxml, ®istry)) | |
headerParams := HeaderParams{ | |
GuardBegin: "#pragma once", | |
GuardEnd: "", | |
Namespace: "vk", | |
} | |
ctx := newContext(®istry) | |
panicIfError(tpl.ExecuteTemplate(output, "header", &headerParams)) | |
panicIfError(tpl.ExecuteTemplate(output, "body", &ctx)) | |
panicIfError(tpl.ExecuteTemplate(output, "footer", &headerParams)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"strings" | |
"text/template" | |
) | |
func line(s string) string { | |
if s != "" { | |
return s + "\n" | |
} | |
return "" | |
} | |
var tpl = template.Must(template.New("").Funcs(template.FuncMap{ | |
"hasPrefix": strings.HasPrefix, | |
"hasSuffix": strings.HasSuffix, | |
"line": line, | |
}).Parse(` | |
{{ define "header" }} | |
{{- .GuardBegin }} | |
#include <cstdint> | |
#include <cstddef> | |
#include <cstring> | |
#include <vulkan/vulkan.h> | |
namespace {{ .Namespace }} { | |
template <typename EnumType, typename T = uint32_t> | |
class Flags { | |
T m_mask; | |
public: | |
Flags(): m_mask(0) {} | |
Flags(EnumType bit): m_mask(static_cast<uint32_t>(bit)) {} | |
explicit Flags(T mask): m_mask(mask) {} | |
Flags(const Flags &rhs): m_mask(rhs.m_mask) {} | |
Flags &operator=(const Flags &rhs) { m_mask = rhs.m_mask; return *this; } | |
Flags &operator|=(const Flags &rhs) { m_mask |= rhs.m_mask; return *this; } | |
Flags &operator&=(const Flags &rhs) { m_mask &= rhs.m_mask; return *this; } | |
Flags &operator^=(const Flags &rhs) { m_mask ^= rhs.m_mask; return *this; } | |
Flags operator|(const Flags &rhs) const { return Flags(m_mask | rhs.m_mask); } | |
Flags operator&(const Flags &rhs) const { return Flags(m_mask & rhs.m_mask); } | |
Flags operator^(const Flags &rhs) const { return Flags(m_mask ^ rhs.m_mask); } | |
Flags operator~() const { return Flags(~m_mask); } | |
bool operator==(const Flags &rhs) const { return m_mask == rhs.m_mask; } | |
bool operator!=(const Flags &rhs) const { return m_mask != rhs.m_mask; } | |
operator bool() const { return m_mask != 0; } | |
explicit operator T() const { return m_mask; } | |
}; | |
template <typename EnumType, typename T> | |
inline Flags<EnumType, T> operator|(EnumType bit, const Flags<EnumType, T> &flags) | |
{ | |
return flags | bit; | |
} | |
template <typename EnumType, typename T> | |
inline Flags<EnumType, T> operator&(EnumType bit, const Flags<EnumType, T> &flags) | |
{ | |
return flags & bit; | |
} | |
template <typename EnumType, typename T> | |
inline Flags<EnumType, T> operator^(EnumType bit, const Flags<EnumType, T> &flags) | |
{ | |
return flags ^ bit; | |
} | |
typedef uint32_t SampleMask; | |
typedef uint32_t Bool32; | |
typedef uint64_t DeviceSize; | |
#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) | |
#define VK_EXPLICIT_HANDLE | |
#else | |
#define VK_EXPLICIT_HANDLE explicit | |
#endif | |
{{ end }} | |
{{ define "footer" }} | |
} // namespace {{ .Namespace }} | |
{{ .GuardEnd -}} | |
{{ end }} | |
{{ define "handle" -}} | |
{{- "\n\n" -}} | |
class {{ .Name }} { | |
{{ .VkName }} m_handle; | |
public: | |
{{ .Name }}(): m_handle(VK_NULL_HANDLE) {} | |
{{ if not .TypeSafe }}VK_EXPLICIT_HANDLE {{ end }}{{ .Name }}({{ .VkName }} handle): m_handle(handle) {} | |
{{ if not .TypeSafe }}VK_EXPLICIT_HANDLE {{ end }}operator {{ .VkName }}() const { return m_handle; } | |
}; | |
{{- end }} | |
{{ define "enum" }} | |
{{- "\n" -}} | |
{{ line .Protect.Begin -}} | |
enum class {{ .Name }} { | |
{{- range .Values }} | |
{{ .Name }} = {{ .VkName }}, | |
{{- end }} | |
}; | |
{{ with $e := . -}} | |
inline const char *getEnumString({{ $e.Name }} e) | |
{ | |
switch (e) { | |
{{ range .Values -}} | |
case {{$e.Name}}::{{.Name}}: return "{{$e.Name}}::{{.Name}}"; | |
{{ end -}} | |
default: return "<invalid enum>"; | |
} | |
} | |
{{- end }} | |
{{ line .Protect.End -}} | |
{{ end }} | |
{{ define "bitmask" }} | |
{{- "\n" -}} | |
{{ line .Protect.Begin -}} | |
{{ template "enum" .Enum }} | |
using {{ .Name }} = Flags<{{ .Enum.Name }}, {{ .VkName }}>; | |
inline {{ .Name }} operator|({{ .Enum.Name }} bit0, {{ .Enum.Name }} bit1) | |
{ | |
return {{ .Name }}(bit0) | bit1; | |
} | |
{{ line .Protect.End -}} | |
{{ end }} | |
{{ define "struct" }} | |
{{- "\n" -}} | |
{{ line .Protect.Begin -}} | |
{{ with $s := . -}} | |
class {{ .Name }} { | |
{{ .VkName }} m_struct; | |
public: | |
{{ .Name }}() | |
{ | |
std::memset(&m_struct, 0, sizeof({{ .VkName }})); | |
{{ if .HasSType -}} | |
m_struct.sType = {{ .TypeName }}; | |
{{- end }} | |
} | |
{{ .Name }}(const {{ .VkName }} &r): m_struct(r) {} | |
{{ range $m := .Members }} | |
{{ if and (not (hasPrefix $m.Type "const ")) $m.AnalyzedType.IsPointer }}const {{ end -}} | |
{{ $m.Type }} {{ $m.Name }}() const | |
{ | |
{{ $m.Converter.VkToCpp $m.AnalyzedType (print "m_struct." $m.Name) }} | |
} | |
{{ if not $s.ReadOnly -}} | |
{{ $s.Name }} &{{ $m.Name }}({{ $m.Type }} {{ $m.Name }}) | |
{ | |
{{ $m.Converter.CppToVk $m.AnalyzedType $m.Name (print "m_struct." $m.Name) }} | |
return *this; | |
} | |
{{- end -}} | |
{{ end }} | |
operator const {{ .VkName }}&() const { return m_struct; } | |
}; | |
{{- end }} | |
{{ .Protect.End -}} | |
{{ end }} | |
{{ define "command" }} | |
{{- "\n" -}} | |
{{ line .Protect.Begin -}} | |
inline {{ .RetType }} {{ .Name }}( | |
{{- range $i, $p := .Parameters -}} | |
{{if $i}}, {{end}}{{$p.Type}} {{$p.Name}} | |
{{- end -}} | |
) | |
{ | |
{{if ne .RetType "void"}}return {{end -}} | |
{{if eq .RetType "Result"}}Result({{end -}} | |
{{ .VkName }}( | |
{{- range $i, $p := .Parameters -}} | |
{{if $i}}, {{end}}{{ $p.Converter.CppToVkArg $p.AnalyzedType $p.Name }} | |
{{- end -}} | |
) | |
{{- if eq .RetType "Result"}}){{end -}} | |
; | |
} | |
{{ line .Protect.End -}} | |
{{ end }} | |
{{ define "body" }} | |
{{ range .Handles -}} | |
{{ template "handle" . }} | |
{{- end }} | |
{{ range .Enums -}} | |
{{ template "enum" . }} | |
{{- end }} | |
{{ range .BitMasks -}} | |
{{ template "bitmask" . }} | |
{{- end }} | |
{{ range .Structs -}} | |
{{ template "struct" . }} | |
{{- end }} | |
{{ range .Commands -}} | |
{{ template "command" . }} | |
{{- end }} | |
{{ end }} | |
`)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment