PageRenderTime 28ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/gpu/gl/interface/gen_interface.go

https://github.com/android/platform_external_skia
Go | 460 lines | 334 code | 48 blank | 78 comment | 103 complexity | d8ce34b59fefbae724d5fa24ac5cc2f1 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, BSD-3-Clause, Apache-2.0
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. package main
  5. // gen_interface creates the assemble/validate cpp files given the
  6. // interface.json5 file.
  7. // See README for more details.
  8. import (
  9. "flag"
  10. "fmt"
  11. "io/ioutil"
  12. "os"
  13. "path/filepath"
  14. "sort"
  15. "strings"
  16. "github.com/flynn/json5"
  17. )
  18. var (
  19. outDir = flag.String("out_dir", "../../src/gpu/gl", "Where to output the GrGlAssembleInterface_* and GrGlInterface.cpp files")
  20. inTable = flag.String("in_table", "./interface.json5", "The JSON5 table to read in")
  21. dryRun = flag.Bool("dryrun", false, "Print the outputs, don't write to file")
  22. )
  23. const (
  24. CORE_FEATURE = "<core>"
  25. SPACER = " "
  26. GLES_FILE_NAME = "GrGLAssembleGLESInterfaceAutogen.cpp"
  27. GL_FILE_NAME = "GrGLAssembleGLInterfaceAutogen.cpp"
  28. WEBGL_FILE_NAME = "GrGLAssembleWebGLInterfaceAutogen.cpp"
  29. INTERFACE_FILE_NAME = "GrGLInterfaceAutogen.cpp"
  30. )
  31. // FeatureSet represents one set of requirements for each of the GL "standards" that
  32. // Skia supports. This is currently OpenGL, OpenGL ES and WebGL.
  33. // OpenGL is typically abbreviated as just "GL".
  34. // https://www.khronos.org/registry/OpenGL/index_gl.php
  35. // https://www.khronos.org/opengles/
  36. // https://www.khronos.org/registry/webgl/specs/1.0/
  37. type FeatureSet struct {
  38. GLReqs []Requirement `json:"GL"`
  39. GLESReqs []Requirement `json:"GLES"`
  40. WebGLReqs []Requirement `json:"WebGL"`
  41. Functions []string `json:"functions"`
  42. HardCodeFunctions []HardCodeFunction `json:"hardcode_functions"`
  43. OptionalFunctions []string `json:"optional"` // not checked in validate
  44. // only assembled/validated when testing
  45. TestOnlyFunctions []string `json:"test_functions"`
  46. Required bool `json:"required"`
  47. }
  48. // Requirement lists how we know if a function exists. Extension is the
  49. // GL extension (or the string CORE_FEATURE if it's part of the core functionality).
  50. // MinVersion optionally indicates the minimum version of a standard
  51. // that has the function.
  52. // SuffixOverride allows the extension suffix to be manually specified instead
  53. // of automatically derived from the extension name.
  54. // (for example, if an NV extension specifies some EXT extensions)
  55. type Requirement struct {
  56. Extension string `json:"ext"` // required
  57. MinVersion *GLVersion `json:"min_version"`
  58. SuffixOverride *string `json:"suffix"`
  59. }
  60. // HardCodeFunction indicates to not use the C++ macro and just directly
  61. // adds a given function ptr to the struct.
  62. type HardCodeFunction struct {
  63. PtrName string `json:"ptr_name"`
  64. CastName string `json:"cast_name"`
  65. GetName string `json:"get_name"`
  66. }
  67. var CORE_REQUIREMENT = Requirement{Extension: CORE_FEATURE, MinVersion: nil}
  68. type GLVersion [2]int
  69. // RequirementGetter functions allows us to "iterate" over the requirements
  70. // of the different standards which are stored as keys in FeatureSet and
  71. // normally not easily iterable.
  72. type RequirementGetter func(FeatureSet) []Requirement
  73. func glRequirements(fs FeatureSet) []Requirement {
  74. return fs.GLReqs
  75. }
  76. func glesRequirements(fs FeatureSet) []Requirement {
  77. return fs.GLESReqs
  78. }
  79. func webglRequirements(fs FeatureSet) []Requirement {
  80. return fs.WebGLReqs
  81. }
  82. // generateAssembleInterface creates one GrGLAssembleInterface_[type]_gen.cpp
  83. // for each of the standards
  84. func generateAssembleInterface(features []FeatureSet) {
  85. gl := fillAssembleTemplate(ASSEMBLE_INTERFACE_GL, features, glRequirements)
  86. writeToFile(*outDir, GL_FILE_NAME, gl)
  87. gles := fillAssembleTemplate(ASSEMBLE_INTERFACE_GL_ES, features, glesRequirements)
  88. writeToFile(*outDir, GLES_FILE_NAME, gles)
  89. webgl := fillAssembleTemplate(ASSEMBLE_INTERFACE_WEBGL, features, webglRequirements)
  90. writeToFile(*outDir, WEBGL_FILE_NAME, webgl)
  91. }
  92. // fillAssembleTemplate returns a generated file given a template (for a single standard)
  93. // to fill out and a slice of features with which to fill it. getReqs is used to select
  94. // the requirements for the standard we are working on.
  95. func fillAssembleTemplate(template string, features []FeatureSet, getReqs RequirementGetter) string {
  96. content := ""
  97. for _, feature := range features {
  98. // For each feature set, we are going to create a series of
  99. // if statements, which check for the requirements (e.g. extensions, version)
  100. // and inside those if branches, write the code to load the
  101. // correct function pointer to the interface. GET_PROC and
  102. // GET_PROC_SUFFIX are macros defined in C++ part of the template
  103. // to accomplish this (for a core feature and extensions, respectively).
  104. reqs := getReqs(feature)
  105. if len(reqs) == 0 {
  106. continue
  107. }
  108. // blocks holds all the if blocks generated - it will be joined with else
  109. // after and appended to content
  110. blocks := []string{}
  111. for i, req := range reqs {
  112. block := ""
  113. ifExpr := requirementIfExpression(req, true)
  114. if ifExpr != "" {
  115. if strings.HasPrefix(ifExpr, "(") {
  116. ifExpr = "if " + ifExpr + " {"
  117. } else {
  118. ifExpr = "if (" + ifExpr + ") {"
  119. }
  120. // Indent the first if statement
  121. if i == 0 {
  122. block = addLine(block, ifExpr)
  123. } else {
  124. block += ifExpr + "\n"
  125. }
  126. }
  127. // sort for determinism
  128. sort.Strings(feature.Functions)
  129. for _, function := range feature.Functions {
  130. block = assembleFunction(block, ifExpr, function, req)
  131. }
  132. sort.Strings(feature.TestOnlyFunctions)
  133. if len(feature.TestOnlyFunctions) > 0 {
  134. block += "#if GR_TEST_UTILS\n"
  135. for _, function := range feature.TestOnlyFunctions {
  136. block = assembleFunction(block, ifExpr, function, req)
  137. }
  138. block += "#endif\n"
  139. }
  140. // a hard code function does not use the C++ macro
  141. for _, hcf := range feature.HardCodeFunctions {
  142. if ifExpr != "" {
  143. // extra tab for being in an if statement
  144. block += SPACER
  145. }
  146. line := fmt.Sprintf(`functions->%s =(%s*)get(ctx, "%s");`, hcf.PtrName, hcf.CastName, hcf.GetName)
  147. block = addLine(block, line)
  148. }
  149. if ifExpr != "" {
  150. block += SPACER + "}"
  151. }
  152. blocks = append(blocks, block)
  153. }
  154. content += strings.Join(blocks, " else ")
  155. if feature.Required && reqs[0] != CORE_REQUIREMENT {
  156. content += ` else {
  157. SkASSERT(false); // Required feature
  158. return nullptr;
  159. }`
  160. }
  161. if !strings.HasSuffix(content, "\n") {
  162. content += "\n"
  163. }
  164. // Add an extra space between blocks for easier readability
  165. content += "\n"
  166. }
  167. return strings.Replace(template, "[[content]]", content, 1)
  168. }
  169. // assembleFunction is a little helper that will add a function to the interface
  170. // using an appropriate macro (e.g. if the function is added)
  171. // with an extension.
  172. func assembleFunction(block, ifExpr, function string, req Requirement) string {
  173. if ifExpr != "" {
  174. // extra tab for being in an if statement
  175. block += SPACER
  176. }
  177. suffix := deriveSuffix(req.Extension)
  178. // Some ARB extensions don't have ARB suffixes because they were written
  179. // for backwards compatibility simultaneous to adding them as required
  180. // in a new GL version.
  181. if suffix == "ARB" {
  182. suffix = ""
  183. }
  184. if req.SuffixOverride != nil {
  185. suffix = *req.SuffixOverride
  186. }
  187. if req.Extension == CORE_FEATURE || suffix == "" {
  188. block = addLine(block, fmt.Sprintf("GET_PROC(%s);", function))
  189. } else if req.Extension != "" {
  190. block = addLine(block, fmt.Sprintf("GET_PROC_SUFFIX(%s, %s);", function, suffix))
  191. }
  192. return block
  193. }
  194. // requirementIfExpression returns a string that is an if expression
  195. // Notably, there is no if expression if the function is a "core" function
  196. // on all supported versions.
  197. // The expressions are wrapped in parentheses so they can be safely
  198. // joined together with && or ||.
  199. func requirementIfExpression(req Requirement, isLocal bool) string {
  200. mv := req.MinVersion
  201. if req == CORE_REQUIREMENT {
  202. return ""
  203. }
  204. if req.Extension == CORE_FEATURE && mv != nil {
  205. return fmt.Sprintf("(glVer >= GR_GL_VER(%d,%d))", mv[0], mv[1])
  206. }
  207. extVar := "fExtensions"
  208. if isLocal {
  209. extVar = "extensions"
  210. }
  211. // We know it has an extension
  212. if req.Extension != "" {
  213. if mv == nil {
  214. return fmt.Sprintf("%s.has(%q)", extVar, req.Extension)
  215. } else {
  216. return fmt.Sprintf("(glVer >= GR_GL_VER(%d,%d) && %s.has(%q))", mv[0], mv[1], extVar, req.Extension)
  217. }
  218. }
  219. abort("ERROR: requirement must have ext")
  220. return "ERROR"
  221. }
  222. // driveSuffix returns the suffix of the function associated with the given
  223. // extension.
  224. func deriveSuffix(ext string) string {
  225. // Some extensions begin with GL_ and then have the actual
  226. // extension like KHR, EXT etc.
  227. ext = strings.TrimPrefix(ext, "GL_")
  228. return strings.Split(ext, "_")[0]
  229. }
  230. // addLine is a little helper function which handles the newline and tab
  231. func addLine(str, line string) string {
  232. return str + SPACER + line + "\n"
  233. }
  234. func writeToFile(parent, file, content string) {
  235. p := filepath.Join(parent, file)
  236. if *dryRun {
  237. fmt.Printf("Writing to %s\n", p)
  238. fmt.Println(content)
  239. } else {
  240. if err := ioutil.WriteFile(p, []byte(content), 0644); err != nil {
  241. abort("Error while writing to file %s: %s", p, err)
  242. }
  243. }
  244. }
  245. // validationEntry is a helper struct that contains anything
  246. // necessary to make validation code for a given standard.
  247. type validationEntry struct {
  248. StandardCheck string
  249. GetReqs RequirementGetter
  250. }
  251. func generateValidateInterface(features []FeatureSet) {
  252. standards := []validationEntry{
  253. {
  254. StandardCheck: "GR_IS_GR_GL(fStandard)",
  255. GetReqs: glRequirements,
  256. }, {
  257. StandardCheck: "GR_IS_GR_GL_ES(fStandard)",
  258. GetReqs: glesRequirements,
  259. }, {
  260. StandardCheck: "GR_IS_GR_WEBGL(fStandard)",
  261. GetReqs: webglRequirements,
  262. },
  263. }
  264. content := ""
  265. // For each feature, we are going to generate a series of
  266. // boolean expressions which check that the functions we thought
  267. // were gathered during the assemble phase actually were applied to
  268. // the interface (functionCheck). This check will be guarded
  269. // another set of if statements (one per standard) based
  270. // on the same requirements (standardChecks) that were used when
  271. // assembling the interface.
  272. for _, feature := range features {
  273. if allReqsAreCore(feature) {
  274. content += functionCheck(feature, 1)
  275. } else {
  276. content += SPACER
  277. standardChecks := []string{}
  278. for _, std := range standards {
  279. reqs := std.GetReqs(feature)
  280. if reqs == nil || len(reqs) == 0 {
  281. continue
  282. }
  283. expr := []string{}
  284. for _, r := range reqs {
  285. e := requirementIfExpression(r, false)
  286. if e != "" {
  287. expr = append(expr, e)
  288. }
  289. }
  290. check := ""
  291. if len(expr) == 0 {
  292. check = fmt.Sprintf("%s", std.StandardCheck)
  293. } else {
  294. lineBreak := "\n" + SPACER + " "
  295. check = fmt.Sprintf("(%s && (%s%s))", std.StandardCheck, lineBreak, strings.Join(expr, " ||"+lineBreak))
  296. }
  297. standardChecks = append(standardChecks, check)
  298. }
  299. content += fmt.Sprintf("if (%s) {\n", strings.Join(standardChecks, " ||\n"+SPACER+" "))
  300. content += functionCheck(feature, 2)
  301. content += SPACER + "}\n"
  302. }
  303. // add additional line between each block
  304. content += "\n"
  305. }
  306. content = strings.Replace(VALIDATE_INTERFACE, "[[content]]", content, 1)
  307. writeToFile(*outDir, INTERFACE_FILE_NAME, content)
  308. }
  309. // functionCheck returns an if statement that checks that all functions
  310. // in the passed in slice are on the interface (that is, they are truthy
  311. // on the fFunctions struct)
  312. func functionCheck(feature FeatureSet, indentLevel int) string {
  313. // sort for determinism
  314. sort.Strings(feature.Functions)
  315. indent := strings.Repeat(SPACER, indentLevel)
  316. checks := []string{}
  317. for _, function := range feature.Functions {
  318. if in(function, feature.OptionalFunctions) {
  319. continue
  320. }
  321. checks = append(checks, "!fFunctions.f"+function)
  322. }
  323. testOnly := []string{}
  324. for _, function := range feature.TestOnlyFunctions {
  325. if in(function, feature.OptionalFunctions) {
  326. continue
  327. }
  328. testOnly = append(testOnly, "!fFunctions.f"+function)
  329. }
  330. for _, hcf := range feature.HardCodeFunctions {
  331. checks = append(checks, "!fFunctions."+hcf.PtrName)
  332. }
  333. preCheck := ""
  334. if len(testOnly) != 0 {
  335. preCheck = fmt.Sprintf(`#if GR_TEST_UTILS
  336. %sif (%s) {
  337. %s%sRETURN_FALSE_INTERFACE;
  338. %s}
  339. #endif
  340. `, indent, strings.Join(testOnly, " ||\n"+indent+" "), indent, SPACER, indent)
  341. }
  342. if len(checks) == 0 {
  343. return preCheck + strings.Repeat(SPACER, indentLevel) + "// all functions were marked optional or test_only\n"
  344. }
  345. return preCheck + fmt.Sprintf(`%sif (%s) {
  346. %s%sRETURN_FALSE_INTERFACE;
  347. %s}
  348. `, indent, strings.Join(checks, " ||\n"+indent+" "), indent, SPACER, indent)
  349. }
  350. // allReqsAreCore returns true iff the FeatureSet is part of "core" for
  351. // all standards
  352. func allReqsAreCore(feature FeatureSet) bool {
  353. if feature.GLReqs == nil || feature.GLESReqs == nil {
  354. return false
  355. }
  356. return feature.GLReqs[0] == CORE_REQUIREMENT && feature.GLESReqs[0] == CORE_REQUIREMENT && feature.WebGLReqs[0] == CORE_REQUIREMENT
  357. }
  358. func validateFeatures(features []FeatureSet) {
  359. seen := map[string]bool{}
  360. for _, feature := range features {
  361. for _, fn := range feature.Functions {
  362. if seen[fn] {
  363. abort("ERROR: Duplicate function %s", fn)
  364. }
  365. seen[fn] = true
  366. }
  367. for _, fn := range feature.TestOnlyFunctions {
  368. if seen[fn] {
  369. abort("ERROR: Duplicate function %s\n", fn)
  370. }
  371. seen[fn] = true
  372. }
  373. }
  374. }
  375. // in returns true if |s| is *in* |a| slice.
  376. func in(s string, a []string) bool {
  377. for _, x := range a {
  378. if x == s {
  379. return true
  380. }
  381. }
  382. return false
  383. }
  384. func abort(fmtStr string, inputs ...interface{}) {
  385. fmt.Printf(fmtStr+"\n", inputs...)
  386. os.Exit(1)
  387. }
  388. func main() {
  389. flag.Parse()
  390. b, err := ioutil.ReadFile(*inTable)
  391. if err != nil {
  392. abort("Could not read file %s", err)
  393. }
  394. dir, err := os.Open(*outDir)
  395. if err != nil {
  396. abort("Could not write to output dir %s", err)
  397. }
  398. defer dir.Close()
  399. if fi, err := dir.Stat(); err != nil {
  400. abort("Error getting info about %s: %s", *outDir, err)
  401. } else if !fi.IsDir() {
  402. abort("%s must be a directory", *outDir)
  403. }
  404. features := []FeatureSet{}
  405. err = json5.Unmarshal(b, &features)
  406. if err != nil {
  407. abort("Invalid JSON: %s", err)
  408. }
  409. validateFeatures(features)
  410. generateAssembleInterface(features)
  411. generateValidateInterface(features)
  412. }