blob: 6c1d422d99c91925b1a30ee933dd73b4cb7f1810 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"os"
"regexp"
"strings"
)
const registry_url = "https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/gl.xml"
// DownloadRegistry downloads the Khronos XML registry file.
func DownloadRegistry() *Registry {
bytes := Download(registry_url)
if len(bytes) == 0 {
panic(fmt.Errorf("Can not download %s", registry_url))
}
reg := &Registry{}
if err := xml.Unmarshal(bytes, reg); err != nil {
panic(err.Error())
}
return reg
}
type KhronosAPI string
type Version string
const GLES1API = KhronosAPI("gles1")
const GLES2API = KhronosAPI("gles2") // Includes GLES 3.0 and later
func (v Version) String() string { return fmt.Sprintf("%s", string(v)) }
type Registry struct {
Group []*Group `xml:"groups>group"`
Enums []*Enums `xml:"enums"`
Command []*Command `xml:"commands>command"`
Feature []*Feature `xml:"feature"`
Extension []*ExtensionElement `xml:"extensions>extension"`
}
type NamedElementList []NamedElement
type NamedElement struct {
Name string `xml:"name,attr"`
}
type Group struct {
NamedElement
Enum NamedElementList `xml:"enum"`
}
type Enums struct {
Namespace string `xml:"namespace,attr"`
Group string `xml:"group,attr"`
Type string `xml:"type,attr"` // "bitmask"
Comment string `xml:"comment,attr"`
Enum []Enum `xml:"enum"`
}
type Enum struct {
NamedElement
Value string `xml:"value,attr"`
Type string `xml:"type,attr"` // "u" or "ull"
API KhronosAPI `xml:"api,attr"`
Alias string `xml:"alias,attr"`
}
type Command struct {
Proto ProtoOrParam `xml:"proto"`
Param []ProtoOrParam `xml:"param"`
Alias NamedElement `xml:"alias"`
}
type ProtoOrParam struct {
InnerXML string `xml:",innerxml"`
Chardata string `xml:",chardata"`
Group string `xml:"group,attr"`
Length string `xml:"len,attr"`
Ptype string `xml:"ptype"`
Name string `xml:"name"`
}
type Feature struct {
NamedElement
API KhronosAPI `xml:"api,attr"`
Number Version `xml:"number,attr"`
Require RequireOrRemoveList `xml:"require"`
Remove RequireOrRemoveList `xml:"remove"`
}
type ExtensionElement struct {
NamedElement
Supported string `xml:"supported,attr"`
Require RequireOrRemoveList `xml:"require"`
Remove RequireOrRemoveList `xml:"remove"`
}
type RequireOrRemoveList []RequireOrRemove
type RequireOrRemove struct {
API KhronosAPI `xml:"api,attr"` // for extensions only
Profile string `xml:"profile,attr"`
Comment string `xml:"comment,attr"`
Enum NamedElementList `xml:"enum"`
Command NamedElementList `xml:"command"`
}
func (l NamedElementList) Contains(name string) bool {
for _, v := range l {
if v.Name == name {
return true
}
}
return false
}
func (r *RequireOrRemove) Contains(name string) bool {
return r.Enum.Contains(name) || r.Command.Contains(name)
}
func (l RequireOrRemoveList) Contains(name string) bool {
for _, v := range l {
if v.Contains(name) {
return true
}
}
return false
}
func (e *ExtensionElement) IsSupported(api KhronosAPI) bool {
for _, v := range strings.Split(e.Supported, "|") {
if KhronosAPI(v) == api {
return true
}
}
return false
}
func (c Command) Name() string {
return c.Proto.Name
}
func (p ProtoOrParam) Type() string {
name := p.InnerXML
name = name[:strings.Index(name, "<name>")]
name = strings.Replace(name, "<ptype>", "", 1)
name = strings.Replace(name, "</ptype>", "", 1)
name = strings.TrimSpace(name)
return name
}
// ParamsAndResult returns all parameters and return value as -1.
func (cmd *Command) ParamsAndResult() map[int]ProtoOrParam {
result := cmd.Proto
result.Name = "result"
results := map[int]ProtoOrParam{-1: result}
for i, param := range cmd.Param {
results[i] = param
}
return results
}
// GetVersions returns sorted list of versions which support the given symbol.
func (r *Registry) GetVersions(api KhronosAPI, name string) []Version {
version, found := Version(""), false
for _, feature := range r.Feature {
if feature.API == api {
if feature.Require.Contains(name) {
if found {
panic(fmt.Errorf("redefinition of %s", name))
}
version, found = feature.Number, true
}
if feature.Remove != nil {
// not used in GLES
panic(fmt.Errorf("remove tag is not supported"))
}
}
}
if found {
switch version {
case "1.0":
return []Version{"1.0"}
case "2.0":
return []Version{"2.0", "3.0", "3.1", "3.2"}
case "3.0":
return []Version{"3.0", "3.1", "3.2"}
case "3.1":
return []Version{"3.1", "3.2"}
case "3.2":
return []Version{"3.2"}
default:
panic(fmt.Errorf("Uknown GLES version: %v", version))
}
} else {
return nil
}
}
// GetExtensions returns extensions which define the given symbol.
func (r *Registry) GetExtensions(api KhronosAPI, name string) []string {
var extensions []string
ExtensionLoop:
for _, extension := range r.Extension {
if extension.IsSupported(api) {
for _, require := range extension.Require {
if require.API == "" || require.API == api {
if require.Contains(name) {
extensions = append(extensions, extension.Name)
// sometimes the extension repeats definition - ignore
continue ExtensionLoop
}
}
}
if extension.Remove != nil {
// not used in GLES
panic(fmt.Errorf("remove tag is not supported"))
}
}
}
return extensions
}
var sufix_re = regexp.MustCompile("(64|)(i_|)(I|)([1-4]|[1-4]x[1-4]|)(x|ub|f|i|ui|fi|i64|)(v|)$")
func GetCoreManpage(version Version, cmdName string) (url string, data []byte) {
var urlFormat string
switch version {
case "1.0":
urlFormat = "https://www.khronos.org/opengles/sdk/1.1/docs/man/%s.xml"
case "2.0":
urlFormat = "https://www.khronos.org/opengles/sdk/docs/man/xhtml/%s.xml"
case "3.0":
urlFormat = "https://www.khronos.org/opengles/sdk/docs/man3/html/%s.xhtml"
case "3.1":
urlFormat = "https://www.khronos.org/opengles/sdk/docs/man31/html/%s.xhtml"
case "3.2":
urlFormat = "https://www.khronos.org/opengles/sdk/docs/man32/html/%s.xhtml"
default:
panic(fmt.Errorf("Uknown api version: %v", version))
}
for _, table := range []struct{ oldPrefix, newPrefix string }{
{"glDisable", "glEnable"},
{"glEnd", "glBegin"},
{"glGetBoolean", "glGet"},
{"glGetFixed", "glGet"},
{"glGetFloat", "glGet"},
{"glGetInteger", "glGet"},
{"glGetnUniform", "glGetUniform"},
{"glMemoryBarrierByRegion", "glMemoryBarrier"},
{"glProgramUniformMatrix", "glProgramUniform"},
{"glReadnPixels", "glReadPixels"},
{"glUniformMatrix", "glUniform"},
{"glUnmapBuffer", "glMapBufferRange"},
{"glVertexAttribIFormat", "glVertexAttribFormat"},
{"glVertexAttribIPointer", "glVertexAttribPointer"},
{"", ""}, // no-op
} {
if strings.HasPrefix(cmdName, table.oldPrefix) {
// Replace prefix
cmdName := table.newPrefix + strings.TrimPrefix(cmdName, table.oldPrefix)
// Try to download URL without suffix
if sufix_re.MatchString(cmdName) {
cmdName := sufix_re.ReplaceAllString(cmdName, "")
url = fmt.Sprintf(urlFormat, cmdName)
if data := Download(url); len(data) > 0 {
return url, data
}
}
// Try to download URL with suffix
url = fmt.Sprintf(urlFormat, cmdName)
if data := Download(url); len(data) > 0 {
return url, data
}
}
}
panic(fmt.Errorf("Failed to find URL for %s", cmdName))
}
func GetExtensionManpage(extension string) (url string, data []byte) {
parts := strings.Split(extension, "_")
vendor := parts[1]
page := strings.Join(parts[2:], "_")
// most of the time the page name includes the vendor prefix, but sometimes it does not
url = fmt.Sprintf("https://www.khronos.org/registry/gles/extensions/%s/%s_%s.txt", vendor, vendor, page)
if data := Download(url); len(data) > 0 {
return url, data
}
url = fmt.Sprintf("https://www.khronos.org/registry/gles/extensions/%s/%s.txt", vendor, page)
if data := Download(url); len(data) > 0 {
return url, data
}
for _, table := range []struct{ extension, page string }{
{"GL_NV_coverage_sample", "NV/EGL_NV_coverage_sample.txt"},
{"GL_NV_depth_nonlinear", "NV/EGL_NV_depth_nonlinear.txt"},
{"GL_NV_EGL_NV_coverage_sample", "NV/EGL_NV_coverage_sample.txt"},
} {
if table.extension == extension {
url = fmt.Sprintf("https://www.khronos.org/registry/gles/extensions/%s", table.page)
if data := Download(url); len(data) > 0 {
return url, data
}
}
}
panic(fmt.Errorf("Failed to find URL for %s", extension))
}
// Download the given URL. Returns empty slice if the page can not be found (404).
func Download(url string) []byte {
filename := url
filename = strings.TrimPrefix(filename, "https://")
filename = strings.Replace(filename, "/", "-", strings.Count(filename, "/")-1)
filename = strings.Replace(filename, "/", string(os.PathSeparator), 1)
filename = *cacheDir + string(os.PathSeparator) + filename
if bytes, err := ioutil.ReadFile(filename); err == nil {
return bytes
}
resp, err := http.Get(url)
if err != nil {
panic(err)
}
bytes := []byte{}
if resp.StatusCode == 200 {
bytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
} else if resp.StatusCode != 404 {
panic(fmt.Errorf("%s: %s", url, resp.Status))
}
resp.Body.Close()
dir := filename[0:strings.LastIndex(filename, string(os.PathSeparator))]
if err := os.MkdirAll(dir, 0750); err != nil {
panic(err)
}
if err := ioutil.WriteFile(filename, bytes, 0666); err != nil {
panic(err)
}
return bytes
}