This unifies vertex and texture data formats into a single base enum class. `TextureFormat` and `VertexFormat` then mask the invalid format for their respective usage. Having a base enum allows casting between `TextureFormat` and `VertexFormat` possible (needed for Buffer Textures). It also makes it easier to write and read data to buffers/textures as each format will have an associated host type. These enum is generated from MACRO expansion. This allow to centralize all information about the formats in one place. This avoid duplicating the list of enums for each backend. This only creates the new enum. Porting older enums will be done in other PRs. Normalized integer CPU format are missing and waiting for #130640 Rel #130632 Pull Request: https://projects.blender.org/blender/blender/pulls/138069
399 lines
11 KiB
Python
399 lines
11 KiB
Python
# SPDX-FileCopyrightText: 2012 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""
|
|
Invocation:
|
|
|
|
export CLANG_BIND_DIR="/dsk/src/llvm/tools/clang/bindings/python"
|
|
export CLANG_LIB_DIR="/opt/llvm/lib"
|
|
|
|
python clang_array_check.py somefile.c -DSOME_DEFINE -I/some/include
|
|
|
|
... defines and includes are optional
|
|
|
|
"""
|
|
__all__ = (
|
|
"main",
|
|
)
|
|
|
|
import sys
|
|
|
|
# delay parsing functions until we need them
|
|
USE_LAZY_INIT = True
|
|
USE_EXACT_COMPARE = False
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# predefined function/arg sizes, handy sometimes, but not complete...
|
|
|
|
defs_precalc = {
|
|
"glColor3bv": {0: 3},
|
|
"glColor4bv": {0: 4},
|
|
|
|
"glColor3ubv": {0: 3},
|
|
"glColor4ubv": {0: 4},
|
|
|
|
"glColor3usv": {0: 3},
|
|
"glColor4usv": {0: 4},
|
|
|
|
"glColor3fv": {0: 3},
|
|
"glColor4fv": {0: 4},
|
|
|
|
"glColor3dv": {0: 3},
|
|
"glColor4dv": {0: 4},
|
|
|
|
"glVertex2fv": {0: 2},
|
|
"glVertex3fv": {0: 3},
|
|
"glVertex4fv": {0: 4},
|
|
|
|
"glEvalCoord1fv": {0: 1},
|
|
"glEvalCoord1dv": {0: 1},
|
|
"glEvalCoord2fv": {0: 2},
|
|
"glEvalCoord2dv": {0: 2},
|
|
|
|
"glRasterPos2dv": {0: 2},
|
|
"glRasterPos3dv": {0: 3},
|
|
"glRasterPos4dv": {0: 4},
|
|
|
|
"glRasterPos2fv": {0: 2},
|
|
"glRasterPos3fv": {0: 3},
|
|
"glRasterPos4fv": {0: 4},
|
|
|
|
"glRasterPos2sv": {0: 2},
|
|
"glRasterPos3sv": {0: 3},
|
|
"glRasterPos4sv": {0: 4},
|
|
|
|
"glTexCoord2fv": {0: 2},
|
|
"glTexCoord3fv": {0: 3},
|
|
"glTexCoord4fv": {0: 4},
|
|
|
|
"glTexCoord2dv": {0: 2},
|
|
"glTexCoord3dv": {0: 3},
|
|
"glTexCoord4dv": {0: 4},
|
|
|
|
"glNormal3fv": {0: 3},
|
|
"glNormal3dv": {0: 3},
|
|
"glNormal3bv": {0: 3},
|
|
"glNormal3iv": {0: 3},
|
|
"glNormal3sv": {0: 3},
|
|
|
|
# GPU immediate mode.
|
|
"immVertex2iv": {1: 2},
|
|
|
|
"immVertex2fv": {1: 2},
|
|
"immVertex3fv": {1: 3},
|
|
|
|
"immAttr2fv": {1: 2},
|
|
"immAttr3fv": {1: 3},
|
|
"immAttr4fv": {1: 4},
|
|
|
|
"immAttr4ubv": {1: 4},
|
|
|
|
"immUniform2fv": {1: 2},
|
|
"immUniform3fv": {1: 3},
|
|
"immUniform4fv": {1: 4},
|
|
|
|
"immUniformColor3fv": {0: 3},
|
|
"immUniformColor4fv": {0: 4},
|
|
|
|
"immUniformColor3ubv": {1: 3},
|
|
"immUniformColor4ubv": {1: 4},
|
|
|
|
"immUniformColor3fvAlpha": {0: 3},
|
|
"immUniformColor4fvAlpha": {0: 4},
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
import sys
|
|
|
|
if 0:
|
|
# Examples with LLVM as the root directory: `/dsk/src/llvm`.
|
|
|
|
# Path containing `clang/__init__.py`.
|
|
CLANG_BIND_DIR = "/dsk/src/llvm/tools/clang/bindings/python"
|
|
|
|
# Path containing `libclang.so`.
|
|
CLANG_LIB_DIR = "/opt/llvm/lib"
|
|
else:
|
|
import os
|
|
CLANG_BIND_DIR = os.environ.get("CLANG_BIND_DIR")
|
|
CLANG_LIB_DIR = os.environ.get("CLANG_LIB_DIR")
|
|
|
|
if CLANG_BIND_DIR is None:
|
|
print("$CLANG_BIND_DIR python binding dir not set")
|
|
if CLANG_LIB_DIR is None:
|
|
print("$CLANG_LIB_DIR clang lib dir not set")
|
|
|
|
if CLANG_BIND_DIR:
|
|
sys.path.append(CLANG_BIND_DIR)
|
|
|
|
import clang
|
|
import clang.cindex
|
|
from clang.cindex import (CursorKind,
|
|
TypeKind,
|
|
TokenKind)
|
|
|
|
if CLANG_LIB_DIR:
|
|
clang.cindex.Config.set_library_path(CLANG_LIB_DIR)
|
|
|
|
index = clang.cindex.Index.create()
|
|
|
|
args = sys.argv[2:]
|
|
# print(args)
|
|
|
|
tu = index.parse(sys.argv[1], args)
|
|
# print('Translation unit: %s' % tu.spelling)
|
|
filepath = tu.spelling
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def function_parm_wash_tokens(parm):
|
|
# print(parm.kind)
|
|
assert parm.kind in (CursorKind.PARM_DECL,
|
|
CursorKind.VAR_DECL, # XXX, double check this
|
|
CursorKind.FIELD_DECL,
|
|
)
|
|
|
|
"""
|
|
Return tokens without trailing commands and 'const'
|
|
"""
|
|
|
|
tokens = [t for t in parm.get_tokens()]
|
|
if not tokens:
|
|
return tokens
|
|
|
|
# if tokens[-1].kind == To
|
|
# remove trailing char
|
|
if tokens[-1].kind == TokenKind.PUNCTUATION:
|
|
if tokens[-1].spelling in {",", ")", ";"}:
|
|
tokens.pop()
|
|
# else:
|
|
# print(tokens[-1].spelling)
|
|
|
|
t_new = []
|
|
for t in tokens:
|
|
t_kind = t.kind
|
|
t_spelling = t.spelling
|
|
ok = True
|
|
if t_kind == TokenKind.KEYWORD:
|
|
if t_spelling in {"const", "restrict", "volatile"}:
|
|
ok = False
|
|
elif t_spelling.startswith("__"):
|
|
ok = False # __restrict
|
|
elif t_kind in (TokenKind.COMMENT, ):
|
|
ok = False
|
|
|
|
# Use these
|
|
elif t_kind in (TokenKind.LITERAL,
|
|
TokenKind.PUNCTUATION,
|
|
TokenKind.IDENTIFIER):
|
|
# use but ignore
|
|
pass
|
|
|
|
else:
|
|
print("Unknown!", t_kind, t_spelling)
|
|
|
|
# if its OK we will add
|
|
if ok:
|
|
t_new.append(t)
|
|
return t_new
|
|
|
|
|
|
def parm_size(node_child):
|
|
tokens = function_parm_wash_tokens(node_child)
|
|
|
|
# print(" ".join([t.spelling for t in tokens]))
|
|
|
|
# NOT PERFECT CODE, EXTRACT SIZE FROM TOKENS
|
|
if len(tokens) >= 3: # foo [ 1 ]
|
|
if ((tokens[-3].kind == TokenKind.PUNCTUATION and tokens[-3].spelling == "[") and
|
|
(tokens[-2].kind == TokenKind.LITERAL and tokens[-2].spelling.isdigit()) and
|
|
(tokens[-1].kind == TokenKind.PUNCTUATION and tokens[-1].spelling == "]")):
|
|
# ---
|
|
return int(tokens[-2].spelling)
|
|
return -1
|
|
|
|
|
|
def function_get_arg_sizes(node):
|
|
# Return a dict if (index: size) items
|
|
# {arg_indx: arg_array_size, ... ]
|
|
arg_sizes = {}
|
|
|
|
if 1: # node.spelling == "BM_vert_create", for debugging
|
|
node_parms = [node_child for node_child in node.get_children()
|
|
if node_child.kind == CursorKind.PARM_DECL]
|
|
|
|
for i, node_child in enumerate(node_parms):
|
|
|
|
# print(node_child.kind, node_child.spelling)
|
|
# print(node_child.type.kind, node_child.spelling)
|
|
if node_child.type.kind == TypeKind.CONSTANTARRAY:
|
|
pointee = node_child.type.get_pointee()
|
|
size = parm_size(node_child)
|
|
if size != -1:
|
|
arg_sizes[i] = size
|
|
|
|
return arg_sizes
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
_defs = {}
|
|
|
|
|
|
def lookup_function_size_def(func_id):
|
|
if USE_LAZY_INIT:
|
|
result = _defs.get(func_id, {})
|
|
if type(result) != dict:
|
|
result = _defs[func_id] = function_get_arg_sizes(result)
|
|
return result
|
|
else:
|
|
return _defs.get(func_id, {})
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def file_check_arg_sizes(tu):
|
|
|
|
# main checking function
|
|
def validate_arg_size(node):
|
|
"""
|
|
Loop over args and validate sizes for args we KNOW the size of.
|
|
"""
|
|
assert node.kind == CursorKind.CALL_EXPR
|
|
|
|
if 0:
|
|
print("---",
|
|
" <~> ".join(
|
|
[" ".join([t.spelling for t in C.get_tokens()])
|
|
for C in node.get_children()]
|
|
))
|
|
# print(node.location)
|
|
|
|
# first child is the function call, skip that.
|
|
children = list(node.get_children())
|
|
|
|
if not children:
|
|
return # XXX, look into this, happens on C++
|
|
|
|
func = children[0]
|
|
|
|
# get the func declaration!
|
|
# works but we can better scan for functions ahead of time.
|
|
if 0:
|
|
func_dec = func.get_definition()
|
|
if func_dec:
|
|
print("FD", " ".join([t.spelling for t in func_dec.get_tokens()]))
|
|
else:
|
|
# HRMP'f - why does this fail?
|
|
print("AA", " ".join([t.spelling for t in node.get_tokens()]))
|
|
else:
|
|
args_size_definition = () # dummy
|
|
|
|
# get the key
|
|
tok = list(func.get_tokens())
|
|
if tok:
|
|
func_id = tok[0].spelling
|
|
args_size_definition = lookup_function_size_def(func_id)
|
|
|
|
if not args_size_definition:
|
|
return
|
|
|
|
children = children[1:]
|
|
for i, node_child in enumerate(children):
|
|
children = list(node_child.get_children())
|
|
|
|
# skip if we don't have an index...
|
|
size_def = args_size_definition.get(i, -1)
|
|
|
|
if size_def == -1:
|
|
continue
|
|
|
|
# print([c.kind for c in children])
|
|
# print(" ".join([t.spelling for t in node_child.get_tokens()]))
|
|
|
|
if len(children) == 1:
|
|
arg = children[0]
|
|
if arg.kind in (CursorKind.DECL_REF_EXPR,
|
|
CursorKind.UNEXPOSED_EXPR):
|
|
|
|
if arg.type.kind == TypeKind.CONSTANTARRAY:
|
|
dec = arg.get_definition()
|
|
if dec:
|
|
size = parm_size(dec)
|
|
|
|
# size == 0 is for 'float *a'
|
|
if size != -1 and size != 0:
|
|
|
|
# nice print!
|
|
if 0:
|
|
print("".join([t.spelling for t in func.get_tokens()]),
|
|
i,
|
|
" ".join([t.spelling for t in dec.get_tokens()]))
|
|
|
|
# testing
|
|
# size_def = 100
|
|
if size != 1:
|
|
if USE_EXACT_COMPARE:
|
|
# is_err = (size != size_def) and (size != 4 and size_def != 3)
|
|
is_err = (size != size_def)
|
|
else:
|
|
is_err = (size < size_def)
|
|
|
|
if is_err:
|
|
location = node.location
|
|
# if "math_color_inline.c" not in str(location.file):
|
|
if 1:
|
|
print("%s:%d:%d: argument %d is size %d, should be %d (from %s)" %
|
|
(location.file,
|
|
location.line,
|
|
location.column,
|
|
i + 1, size, size_def,
|
|
filepath # always the same but useful when running threaded
|
|
))
|
|
|
|
# we don't really care what we are looking at, just scan entire file for
|
|
# function calls.
|
|
|
|
def recursive_func_call_check(node):
|
|
if node.kind == CursorKind.CALL_EXPR:
|
|
validate_arg_size(node)
|
|
|
|
for c in node.get_children():
|
|
recursive_func_call_check(c)
|
|
|
|
recursive_func_call_check(tu.cursor)
|
|
|
|
|
|
# -- first pass, cache function definitions sizes
|
|
|
|
# PRINT FUNC DEFINES
|
|
def recursive_arg_sizes(node, ):
|
|
# print(node.kind, node.spelling)
|
|
if node.kind == CursorKind.FUNCTION_DECL:
|
|
if USE_LAZY_INIT:
|
|
args_sizes = node
|
|
else:
|
|
args_sizes = function_get_arg_sizes(node)
|
|
# if args_sizes:
|
|
# print(node.spelling, args_sizes)
|
|
_defs[node.spelling] = args_sizes
|
|
# print("adding", node.spelling)
|
|
for c in node.get_children():
|
|
recursive_arg_sizes(c)
|
|
|
|
|
|
def main() -> int:
|
|
# cache function sizes
|
|
recursive_arg_sizes(tu.cursor)
|
|
_defs.update(defs_precalc)
|
|
|
|
# --- second pass, check against def's
|
|
file_check_arg_sizes(tu)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main)
|