fc1dddbb8d
This reverts commit753836fcad
. Reason for revert: fixed Original change's description: > Revert "Get EGLimage functions out of GrGLInterface." > > This reverts commitbc233135e4
. > > Reason for revert: > EGLImageTest and TextureBindingTest broken on Windows ANGLE > Build failures on Debian9, unable to link > ld.lld: error: undefined symbol: eglCreateImageKHR > > Original change's description: > > Get EGLimage functions out of GrGLInterface. > > > > Only used in test code which can just directly call these. > > > > Also removed related code in interface assemble/validate generator. > > > > Change-Id: I7e821ebb9328bdd2a54d8ab3af929399ca0517d5 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/213480 > > Commit-Queue: Robert Phillips <robertphillips@google.com> > > Reviewed-by: Robert Phillips <robertphillips@google.com> > > TBR=bsalomon@google.com,robertphillips@google.com > > Change-Id: I622fadf22c5d79f5b1d335a8a81455978d49a86a > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/213960 > Reviewed-by: Michael Ludwig <michaelludwig@google.com> > Commit-Queue: Michael Ludwig <michaelludwig@google.com> TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com EXTRA_TRYBOTS=Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All-ANGLE Change-Id: I71397072fa79ae3c72f3835c5e991b9ef2465a9a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/214040 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Michael Ludwig <michaelludwig@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
461 lines
14 KiB
Go
461 lines
14 KiB
Go
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
// gen_interface creates the assemble/validate cpp files given the
|
|
// interface.json5 file.
|
|
// See README for more details.
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/flynn/json5"
|
|
)
|
|
|
|
var (
|
|
outDir = flag.String("out_dir", "../../src/gpu/gl", "Where to output the GrGlAssembleInterface_* and GrGlInterface.cpp files")
|
|
inTable = flag.String("in_table", "./interface.json5", "The JSON5 table to read in")
|
|
dryRun = flag.Bool("dryrun", false, "Print the outputs, don't write to file")
|
|
)
|
|
|
|
const (
|
|
CORE_FEATURE = "<core>"
|
|
SPACER = " "
|
|
GLES_FILE_NAME = "GrGLAssembleGLESInterfaceAutogen.cpp"
|
|
GL_FILE_NAME = "GrGLAssembleGLInterfaceAutogen.cpp"
|
|
WEBGL_FILE_NAME = "GrGLAssembleWebGLInterfaceAutogen.cpp"
|
|
INTERFACE_FILE_NAME = "GrGLInterfaceAutogen.cpp"
|
|
)
|
|
|
|
// FeatureSet represents one set of requirements for each of the GL "standards" that
|
|
// Skia supports. This is currently OpenGL, OpenGL ES and WebGL.
|
|
// OpenGL is typically abbreviated as just "GL".
|
|
// https://www.khronos.org/registry/OpenGL/index_gl.php
|
|
// https://www.khronos.org/opengles/
|
|
// https://www.khronos.org/registry/webgl/specs/1.0/
|
|
type FeatureSet struct {
|
|
GLReqs []Requirement `json:"GL"`
|
|
GLESReqs []Requirement `json:"GLES"`
|
|
WebGLReqs []Requirement `json:"WebGL"`
|
|
|
|
Functions []string `json:"functions"`
|
|
HardCodeFunctions []HardCodeFunction `json:"hardcode_functions"`
|
|
OptionalFunctions []string `json:"optional"` // not checked in validate
|
|
|
|
// only assembled/validated when testing
|
|
TestOnlyFunctions []string `json:"test_functions"`
|
|
|
|
Required bool `json:"required"`
|
|
}
|
|
|
|
// Requirement lists how we know if a function exists. Extension is the
|
|
// GL extension (or the string CORE_FEATURE if it's part of the core functionality).
|
|
// MinVersion optionally indicates the minimum version of a standard
|
|
// that has the function.
|
|
// SuffixOverride allows the extension suffix to be manually specified instead
|
|
// of automatically derived from the extension name.
|
|
// (for example, if an NV extension specifies some EXT extensions)
|
|
type Requirement struct {
|
|
Extension string `json:"ext"` // required
|
|
MinVersion *GLVersion `json:"min_version"`
|
|
SuffixOverride *string `json:"suffix"`
|
|
}
|
|
|
|
// HardCodeFunction indicates to not use the C++ macro and just directly
|
|
// adds a given function ptr to the struct.
|
|
type HardCodeFunction struct {
|
|
PtrName string `json:"ptr_name"`
|
|
CastName string `json:"cast_name"`
|
|
GetName string `json:"get_name"`
|
|
}
|
|
|
|
var CORE_REQUIREMENT = Requirement{Extension: CORE_FEATURE, MinVersion: nil}
|
|
|
|
type GLVersion [2]int
|
|
|
|
// RequirementGetter functions allows us to "iterate" over the requirements
|
|
// of the different standards which are stored as keys in FeatureSet and
|
|
// normally not easily iterable.
|
|
type RequirementGetter func(FeatureSet) []Requirement
|
|
|
|
func glRequirements(fs FeatureSet) []Requirement {
|
|
return fs.GLReqs
|
|
}
|
|
|
|
func glesRequirements(fs FeatureSet) []Requirement {
|
|
return fs.GLESReqs
|
|
}
|
|
|
|
func webglRequirements(fs FeatureSet) []Requirement {
|
|
return fs.WebGLReqs
|
|
}
|
|
|
|
// generateAssembleInterface creates one GrGLAssembleInterface_[type]_gen.cpp
|
|
// for each of the standards
|
|
func generateAssembleInterface(features []FeatureSet) {
|
|
gl := fillAssembleTemplate(ASSEMBLE_INTERFACE_GL, features, glRequirements)
|
|
writeToFile(*outDir, GL_FILE_NAME, gl)
|
|
gles := fillAssembleTemplate(ASSEMBLE_INTERFACE_GL_ES, features, glesRequirements)
|
|
writeToFile(*outDir, GLES_FILE_NAME, gles)
|
|
webgl := fillAssembleTemplate(ASSEMBLE_INTERFACE_WEBGL, features, webglRequirements)
|
|
writeToFile(*outDir, WEBGL_FILE_NAME, webgl)
|
|
}
|
|
|
|
// fillAssembleTemplate returns a generated file given a template (for a single standard)
|
|
// to fill out and a slice of features with which to fill it. getReqs is used to select
|
|
// the requirements for the standard we are working on.
|
|
func fillAssembleTemplate(template string, features []FeatureSet, getReqs RequirementGetter) string {
|
|
content := ""
|
|
for _, feature := range features {
|
|
// For each feature set, we are going to create a series of
|
|
// if statements, which check for the requirements (e.g. extensions, version)
|
|
// and inside those if branches, write the code to load the
|
|
// correct function pointer to the interface. GET_PROC and
|
|
// GET_PROC_SUFFIX are macros defined in C++ part of the template
|
|
// to accomplish this (for a core feature and extensions, respectively).
|
|
reqs := getReqs(feature)
|
|
if len(reqs) == 0 {
|
|
continue
|
|
}
|
|
// blocks holds all the if blocks generated - it will be joined with else
|
|
// after and appended to content
|
|
blocks := []string{}
|
|
for i, req := range reqs {
|
|
block := ""
|
|
ifExpr := requirementIfExpression(req, true)
|
|
|
|
if ifExpr != "" {
|
|
if strings.HasPrefix(ifExpr, "(") {
|
|
ifExpr = "if " + ifExpr + " {"
|
|
} else {
|
|
ifExpr = "if (" + ifExpr + ") {"
|
|
}
|
|
// Indent the first if statement
|
|
if i == 0 {
|
|
block = addLine(block, ifExpr)
|
|
} else {
|
|
block += ifExpr + "\n"
|
|
}
|
|
}
|
|
// sort for determinism
|
|
sort.Strings(feature.Functions)
|
|
for _, function := range feature.Functions {
|
|
block = assembleFunction(block, ifExpr, function, req)
|
|
}
|
|
sort.Strings(feature.TestOnlyFunctions)
|
|
if len(feature.TestOnlyFunctions) > 0 {
|
|
block += "#if GR_TEST_UTILS\n"
|
|
for _, function := range feature.TestOnlyFunctions {
|
|
block = assembleFunction(block, ifExpr, function, req)
|
|
}
|
|
block += "#endif\n"
|
|
}
|
|
|
|
// a hard code function does not use the C++ macro
|
|
for _, hcf := range feature.HardCodeFunctions {
|
|
if ifExpr != "" {
|
|
// extra tab for being in an if statement
|
|
block += SPACER
|
|
}
|
|
line := fmt.Sprintf(`functions->%s =(%s*)get(ctx, "%s");`, hcf.PtrName, hcf.CastName, hcf.GetName)
|
|
block = addLine(block, line)
|
|
}
|
|
if ifExpr != "" {
|
|
block += SPACER + "}"
|
|
}
|
|
blocks = append(blocks, block)
|
|
}
|
|
content += strings.Join(blocks, " else ")
|
|
|
|
if feature.Required && reqs[0] != CORE_REQUIREMENT {
|
|
content += ` else {
|
|
SkASSERT(false); // Required feature
|
|
return nullptr;
|
|
}`
|
|
}
|
|
|
|
if !strings.HasSuffix(content, "\n") {
|
|
content += "\n"
|
|
}
|
|
// Add an extra space between blocks for easier readability
|
|
content += "\n"
|
|
|
|
}
|
|
|
|
return strings.Replace(template, "[[content]]", content, 1)
|
|
}
|
|
|
|
// assembleFunction is a little helper that will add a function to the interface
|
|
// using an appropriate macro (e.g. if the function is added)
|
|
// with an extension.
|
|
func assembleFunction(block, ifExpr, function string, req Requirement) string {
|
|
if ifExpr != "" {
|
|
// extra tab for being in an if statement
|
|
block += SPACER
|
|
}
|
|
suffix := deriveSuffix(req.Extension)
|
|
// Some ARB extensions don't have ARB suffixes because they were written
|
|
// for backwards compatibility simultaneous to adding them as required
|
|
// in a new GL version.
|
|
if suffix == "ARB" {
|
|
suffix = ""
|
|
}
|
|
if req.SuffixOverride != nil {
|
|
suffix = *req.SuffixOverride
|
|
}
|
|
if req.Extension == CORE_FEATURE || suffix == "" {
|
|
block = addLine(block, fmt.Sprintf("GET_PROC(%s);", function))
|
|
} else if req.Extension != "" {
|
|
block = addLine(block, fmt.Sprintf("GET_PROC_SUFFIX(%s, %s);", function, suffix))
|
|
}
|
|
return block
|
|
}
|
|
|
|
// requirementIfExpression returns a string that is an if expression
|
|
// Notably, there is no if expression if the function is a "core" function
|
|
// on all supported versions.
|
|
// The expressions are wrapped in parentheses so they can be safely
|
|
// joined together with && or ||.
|
|
func requirementIfExpression(req Requirement, isLocal bool) string {
|
|
mv := req.MinVersion
|
|
if req == CORE_REQUIREMENT {
|
|
return ""
|
|
}
|
|
if req.Extension == CORE_FEATURE && mv != nil {
|
|
return fmt.Sprintf("(glVer >= GR_GL_VER(%d,%d))", mv[0], mv[1])
|
|
}
|
|
extVar := "fExtensions"
|
|
if isLocal {
|
|
extVar = "extensions"
|
|
}
|
|
// We know it has an extension
|
|
if req.Extension != "" {
|
|
if mv == nil {
|
|
return fmt.Sprintf("%s.has(%q)", extVar, req.Extension)
|
|
} else {
|
|
return fmt.Sprintf("(glVer >= GR_GL_VER(%d,%d) && %s.has(%q))", mv[0], mv[1], extVar, req.Extension)
|
|
}
|
|
}
|
|
abort("ERROR: requirement must have ext")
|
|
return "ERROR"
|
|
}
|
|
|
|
// driveSuffix returns the suffix of the function associated with the given
|
|
// extension.
|
|
func deriveSuffix(ext string) string {
|
|
// Some extensions begin with GL_ and then have the actual
|
|
// extension like KHR, EXT etc.
|
|
ext = strings.TrimPrefix(ext, "GL_")
|
|
return strings.Split(ext, "_")[0]
|
|
}
|
|
|
|
// addLine is a little helper function which handles the newline and tab
|
|
func addLine(str, line string) string {
|
|
return str + SPACER + line + "\n"
|
|
}
|
|
|
|
func writeToFile(parent, file, content string) {
|
|
p := filepath.Join(parent, file)
|
|
if *dryRun {
|
|
fmt.Printf("Writing to %s\n", p)
|
|
fmt.Println(content)
|
|
} else {
|
|
if err := ioutil.WriteFile(p, []byte(content), 0644); err != nil {
|
|
abort("Error while writing to file %s: %s", p, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// validationEntry is a helper struct that contains anything
|
|
// necessary to make validation code for a given standard.
|
|
type validationEntry struct {
|
|
StandardCheck string
|
|
GetReqs RequirementGetter
|
|
}
|
|
|
|
func generateValidateInterface(features []FeatureSet) {
|
|
standards := []validationEntry{
|
|
{
|
|
StandardCheck: "GR_IS_GR_GL(fStandard)",
|
|
GetReqs: glRequirements,
|
|
}, {
|
|
StandardCheck: "GR_IS_GR_GL_ES(fStandard)",
|
|
GetReqs: glesRequirements,
|
|
}, {
|
|
StandardCheck: "GR_IS_GR_WEBGL(fStandard)",
|
|
GetReqs: webglRequirements,
|
|
},
|
|
}
|
|
content := ""
|
|
// For each feature, we are going to generate a series of
|
|
// boolean expressions which check that the functions we thought
|
|
// were gathered during the assemble phase actually were applied to
|
|
// the interface (functionCheck). This check will be guarded
|
|
// another set of if statements (one per standard) based
|
|
// on the same requirements (standardChecks) that were used when
|
|
// assembling the interface.
|
|
for _, feature := range features {
|
|
if allReqsAreCore(feature) {
|
|
content += functionCheck(feature, 1)
|
|
} else {
|
|
content += SPACER
|
|
standardChecks := []string{}
|
|
for _, std := range standards {
|
|
reqs := std.GetReqs(feature)
|
|
if reqs == nil || len(reqs) == 0 {
|
|
continue
|
|
}
|
|
expr := []string{}
|
|
for _, r := range reqs {
|
|
e := requirementIfExpression(r, false)
|
|
if e != "" {
|
|
expr = append(expr, e)
|
|
}
|
|
}
|
|
check := ""
|
|
if len(expr) == 0 {
|
|
check = fmt.Sprintf("%s", std.StandardCheck)
|
|
} else {
|
|
lineBreak := "\n" + SPACER + " "
|
|
check = fmt.Sprintf("(%s && (%s%s))", std.StandardCheck, lineBreak, strings.Join(expr, " ||"+lineBreak))
|
|
}
|
|
standardChecks = append(standardChecks, check)
|
|
}
|
|
content += fmt.Sprintf("if (%s) {\n", strings.Join(standardChecks, " ||\n"+SPACER+" "))
|
|
content += functionCheck(feature, 2)
|
|
|
|
content += SPACER + "}\n"
|
|
}
|
|
// add additional line between each block
|
|
content += "\n"
|
|
}
|
|
content = strings.Replace(VALIDATE_INTERFACE, "[[content]]", content, 1)
|
|
writeToFile(*outDir, INTERFACE_FILE_NAME, content)
|
|
}
|
|
|
|
// functionCheck returns an if statement that checks that all functions
|
|
// in the passed in slice are on the interface (that is, they are truthy
|
|
// on the fFunctions struct)
|
|
func functionCheck(feature FeatureSet, indentLevel int) string {
|
|
// sort for determinism
|
|
sort.Strings(feature.Functions)
|
|
indent := strings.Repeat(SPACER, indentLevel)
|
|
|
|
checks := []string{}
|
|
for _, function := range feature.Functions {
|
|
if in(function, feature.OptionalFunctions) {
|
|
continue
|
|
}
|
|
checks = append(checks, "!fFunctions.f"+function)
|
|
}
|
|
testOnly := []string{}
|
|
for _, function := range feature.TestOnlyFunctions {
|
|
if in(function, feature.OptionalFunctions) {
|
|
continue
|
|
}
|
|
testOnly = append(testOnly, "!fFunctions.f"+function)
|
|
}
|
|
for _, hcf := range feature.HardCodeFunctions {
|
|
checks = append(checks, "!fFunctions."+hcf.PtrName)
|
|
}
|
|
preCheck := ""
|
|
if len(testOnly) != 0 {
|
|
preCheck = fmt.Sprintf(`#if GR_TEST_UTILS
|
|
%sif (%s) {
|
|
%s%sRETURN_FALSE_INTERFACE;
|
|
%s}
|
|
#endif
|
|
`, indent, strings.Join(testOnly, " ||\n"+indent+" "), indent, SPACER, indent)
|
|
}
|
|
|
|
if len(checks) == 0 {
|
|
return preCheck + strings.Repeat(SPACER, indentLevel) + "// all functions were marked optional or test_only\n"
|
|
}
|
|
|
|
return preCheck + fmt.Sprintf(`%sif (%s) {
|
|
%s%sRETURN_FALSE_INTERFACE;
|
|
%s}
|
|
`, indent, strings.Join(checks, " ||\n"+indent+" "), indent, SPACER, indent)
|
|
}
|
|
|
|
// allReqsAreCore returns true iff the FeatureSet is part of "core" for
|
|
// all standards
|
|
func allReqsAreCore(feature FeatureSet) bool {
|
|
if feature.GLReqs == nil || feature.GLESReqs == nil {
|
|
return false
|
|
}
|
|
return feature.GLReqs[0] == CORE_REQUIREMENT && feature.GLESReqs[0] == CORE_REQUIREMENT && feature.WebGLReqs[0] == CORE_REQUIREMENT
|
|
}
|
|
|
|
func validateFeatures(features []FeatureSet) {
|
|
seen := map[string]bool{}
|
|
for _, feature := range features {
|
|
for _, fn := range feature.Functions {
|
|
if seen[fn] {
|
|
abort("ERROR: Duplicate function %s", fn)
|
|
}
|
|
seen[fn] = true
|
|
}
|
|
for _, fn := range feature.TestOnlyFunctions {
|
|
if seen[fn] {
|
|
abort("ERROR: Duplicate function %s\n", fn)
|
|
}
|
|
seen[fn] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// in returns true if |s| is *in* |a| slice.
|
|
func in(s string, a []string) bool {
|
|
for _, x := range a {
|
|
if x == s {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func abort(fmtStr string, inputs ...interface{}) {
|
|
fmt.Printf(fmtStr+"\n", inputs...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
b, err := ioutil.ReadFile(*inTable)
|
|
if err != nil {
|
|
abort("Could not read file %s", err)
|
|
}
|
|
|
|
dir, err := os.Open(*outDir)
|
|
if err != nil {
|
|
abort("Could not write to output dir %s", err)
|
|
}
|
|
defer dir.Close()
|
|
if fi, err := dir.Stat(); err != nil {
|
|
abort("Error getting info about %s: %s", *outDir, err)
|
|
} else if !fi.IsDir() {
|
|
abort("%s must be a directory", *outDir)
|
|
}
|
|
|
|
features := []FeatureSet{}
|
|
|
|
err = json5.Unmarshal(b, &features)
|
|
if err != nil {
|
|
abort("Invalid JSON: %s", err)
|
|
}
|
|
|
|
validateFeatures(features)
|
|
|
|
generateAssembleInterface(features)
|
|
generateValidateInterface(features)
|
|
}
|