UI: Console Text Operations #108626
@ -59,6 +59,7 @@ Static Source Code Checking
|
||||
|
||||
* check_cppcheck: Run blender source through cppcheck (C & C++).
|
||||
* check_clang_array: Run blender source through clang array checking script (C & C++).
|
||||
* check_struct_comments: Check struct member comments are correct (C & C++).
|
||||
* check_deprecated: Check if there is any deprecated code to remove.
|
||||
* check_descriptions: Check for duplicate/invalid descriptions.
|
||||
* check_licenses: Check license headers follow the SPDX license specification,
|
||||
@ -468,6 +469,13 @@ check_cppcheck: .FORCE
|
||||
"$(BLENDER_DIR)/check_cppcheck.txt"
|
||||
@echo "written: check_cppcheck.txt"
|
||||
|
||||
check_struct_comments: .FORCE
|
||||
@$(CMAKE_CONFIG)
|
||||
@cd "$(BUILD_DIR)" ; \
|
||||
$(PYTHON) \
|
||||
"$(BLENDER_DIR)/build_files/cmake/cmake_static_check_clang.py" \
|
||||
--checks=struct_comments --match=".*"
|
||||
|
||||
check_clang_array: .FORCE
|
||||
@$(CMAKE_CONFIG)
|
||||
@cd "$(BUILD_DIR)" ; \
|
||||
|
544
build_files/cmake/cmake_static_check_clang.py
Normal file
544
build_files/cmake/cmake_static_check_clang.py
Normal file
@ -0,0 +1,544 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
"""
|
||||
A command line utility to check Blender's source code with CLANG's Python module.
|
||||
|
||||
To call this directly:
|
||||
|
||||
export CLANG_LIB_DIR=/usr/lib64
|
||||
cd {BUILD_DIR}
|
||||
python ../blender/build_files/cmake/cmake_static_check_clang.py --match=".*" --checks=struct_comments
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
Type,
|
||||
Sequence,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
|
||||
import project_source_info
|
||||
|
||||
# pylint: disable-next=import-outside-toplevel
|
||||
import clang # type: ignore
|
||||
# pylint: disable-next=import-outside-toplevel
|
||||
import clang.cindex # type: ignore
|
||||
from clang.cindex import (
|
||||
CursorKind,
|
||||
)
|
||||
|
||||
# Only for readability.
|
||||
ClangNode = Any
|
||||
ClangTranslationUnit = Any
|
||||
ClangSourceLocation = Any
|
||||
|
||||
|
||||
USE_VERBOSE = os.environ.get("VERBOSE", None) is not None
|
||||
|
||||
# Turn off for debugging.
|
||||
USE_MULTIPROCESS = True
|
||||
|
||||
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_LIB_DIR:
|
||||
clang.cindex.Config.set_library_path(CLANG_LIB_DIR)
|
||||
if CLANG_BIND_DIR:
|
||||
sys.path.append(CLANG_BIND_DIR)
|
||||
|
||||
|
||||
CHECKER_IGNORE_PREFIX = [
|
||||
"extern",
|
||||
]
|
||||
|
||||
CHECKER_EXCLUDE_SOURCE_FILES = set(os.path.join(*f.split("/")) for f in (
|
||||
# Skip parsing these large (mostly data files).
|
||||
"source/blender/editors/space_text/text_format_pov.cc",
|
||||
"source/blender/editors/space_text/text_format_pov_ini.cc",
|
||||
))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Utility Functions
|
||||
|
||||
def clang_source_location_as_str(source_location: ClangSourceLocation) -> str:
|
||||
return "{:s}:{:d}:{:d}:".format(str(source_location.file), source_location.line, source_location.column)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checkers
|
||||
|
||||
class ClangChecker:
|
||||
"""
|
||||
Base class for checkers.
|
||||
|
||||
Notes:
|
||||
|
||||
- The function ``check_source`` takes file_data as bytes instead of a string
|
||||
because the offsets provided by CLANG are byte offsets.
|
||||
While the offsets could be converted into UNICODE offset's,
|
||||
there doesn't seem to be an efficient & convenient way to do that.
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, *args: Tuple[Any], **kwargs: Dict[str, Any]) -> Any:
|
||||
raise RuntimeError("%s should not be instantiated" % cls)
|
||||
|
||||
@staticmethod
|
||||
def check_source(
|
||||
_filepath: str,
|
||||
_file_data: bytes,
|
||||
_tu: ClangTranslationUnit,
|
||||
_shared_check_data: Any,
|
||||
) -> List[str]:
|
||||
raise RuntimeError("This function must be overridden by it's subclass!")
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def setup() -> Any:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def teardown(_shared_check_data: Any) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class clang_checkers:
|
||||
# fake module.
|
||||
|
||||
class struct_comments(ClangChecker):
|
||||
"""
|
||||
Ensure comments in struct declarations match the members of the struct, e.g:
|
||||
|
||||
SomeStruct var = {
|
||||
/*name*/ "Text",
|
||||
/*children*/ nullptr,
|
||||
/*flag*/ 0,
|
||||
};
|
||||
|
||||
Will generate a warning if any of the names in the prefix comments don't match the struct member names.
|
||||
"""
|
||||
|
||||
_struct_comments_ignore = {
|
||||
# `PyTypeObject` uses compile time members that vary (see: #PyVarObject_HEAD_INIT macro)
|
||||
# While some clever comment syntax could be supported to signify multiple/optional members
|
||||
# this is such a specific case that it's simpler to skip this warning.
|
||||
"PyTypeObject": {"ob_base": {"ob_size"}},
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _struct_check_comments_recursive(
|
||||
# Static (unchanged for each recursion).
|
||||
filepath: str,
|
||||
file_data: bytes,
|
||||
# Different for each recursion.
|
||||
node: ClangNode,
|
||||
node_parent: ClangNode,
|
||||
level: int,
|
||||
# Used to build data.
|
||||
struct_decl_map: Dict[str, ClangNode],
|
||||
struct_type_map: Dict[str, str],
|
||||
output: List[str],
|
||||
) -> None:
|
||||
|
||||
# Needed to read back the node.
|
||||
if USE_VERBOSE:
|
||||
print("TRY:", node.kind, node.spelling, len(list(node.get_tokens())), level, node.location)
|
||||
|
||||
# if node.kind == CursorKind.VAR_DECL and node.spelling == "Vector_NumMethods":
|
||||
# import IPython
|
||||
# IPython.embed()
|
||||
|
||||
if node.kind == CursorKind.STRUCT_DECL:
|
||||
struct_type = node.spelling.strip()
|
||||
if not struct_type:
|
||||
# The parent may be a `typedef [..] TypeID` where `[..]` is `struct { a; b; c; }`.
|
||||
# Inspect the parent.
|
||||
if node_parent is not None and (node_parent.kind == CursorKind.TYPEDEF_DECL):
|
||||
tokens = list(node_parent.get_tokens())
|
||||
if tokens[0].spelling == "typedef":
|
||||
struct_type = tokens[-1].spelling
|
||||
|
||||
struct_decl_map[struct_type] = node
|
||||
|
||||
# Ignore declarations for anything defined outside this file.
|
||||
if str(node.location.file) == filepath:
|
||||
if node.kind == CursorKind.INIT_LIST_EXPR:
|
||||
if USE_VERBOSE:
|
||||
print(node.spelling, node.location)
|
||||
# Split to avoid `const struct` .. and similar.
|
||||
# NOTE: there may be an array size suffix, e.g. `[4]`.
|
||||
# This could be supported.
|
||||
struct_type = node.type.spelling.split()[-1]
|
||||
struct = struct_decl_map.get(struct_type)
|
||||
if struct is None:
|
||||
if USE_VERBOSE:
|
||||
print("NOT FOUND:", struct_type)
|
||||
struct_type = struct_type_map.get(struct_type)
|
||||
if struct_type is not None:
|
||||
struct = struct_decl_map.get(struct_type)
|
||||
|
||||
if USE_VERBOSE:
|
||||
print("INSPECTING STRUCT:", struct_type)
|
||||
if struct is not None:
|
||||
member_names = [
|
||||
node_child.spelling for node_child in struct.get_children()
|
||||
if node_child.kind == CursorKind.FIELD_DECL
|
||||
]
|
||||
# if struct_type == "PyMappingMethods":
|
||||
# import IPython
|
||||
# IPython.embed()
|
||||
|
||||
children = list(node.get_children())
|
||||
comment_names = []
|
||||
|
||||
# Set to true when there is a comment directly before a value,
|
||||
# this is needed because:
|
||||
# - Comments on the previous line are rarely intended to be identifiers of the struct member.
|
||||
# - Comments which _are_ intended to be identifiers can be wrapped onto new-lines
|
||||
# so they should not be ignored.
|
||||
#
|
||||
# While it's possible every member is wrapped onto a new-line,
|
||||
# this is highly unlikely.
|
||||
comment_names_prefix_any = False
|
||||
|
||||
for node_child in children:
|
||||
# Extract the content before the child
|
||||
# (typically a C-style comment containing the struct member).
|
||||
end = min(node_child.location.offset, len(file_data))
|
||||
|
||||
# It's possible this ID has a preceding "name::space::etc"
|
||||
# which should be skipped.
|
||||
while end > 0 and ((ch := bytes((file_data[end - 1],))).isalpha() or ch == b":"):
|
||||
end -= 1
|
||||
|
||||
has_newline = False
|
||||
while end > 0:
|
||||
ch = bytes((file_data[end - 1],))
|
||||
if ch in {b"\t", b" "}:
|
||||
end -= 1
|
||||
elif ch == b"\n":
|
||||
end -= 1
|
||||
has_newline = True
|
||||
else:
|
||||
break
|
||||
|
||||
beg = end - 1
|
||||
while beg != 0 and bytes((file_data[beg],)) != b"\n":
|
||||
beg -= 1
|
||||
text = file_data[beg:end]
|
||||
if text.lstrip().startswith(b"/*"):
|
||||
if not has_newline:
|
||||
comment_names_prefix_any = True
|
||||
else:
|
||||
text = b""
|
||||
comment_names.append(text.decode('utf-8'))
|
||||
|
||||
if USE_VERBOSE:
|
||||
print(member_names)
|
||||
print(comment_names)
|
||||
|
||||
total = min(len(member_names), len(comment_names))
|
||||
|
||||
if total != 0 and comment_names_prefix_any:
|
||||
result = [""] * total
|
||||
count_found = 0
|
||||
count_invalid = 0
|
||||
for i in range(total):
|
||||
comment = comment_names[i]
|
||||
if "/*" in comment and "*/" in comment:
|
||||
comment = comment.strip().strip("/").strip("*")
|
||||
if comment == member_names[i]:
|
||||
count_found += 1
|
||||
else:
|
||||
suppress_warning = False
|
||||
if (
|
||||
skip_members_table :=
|
||||
clang_checkers.struct_comments._struct_comments_ignore.get(
|
||||
node_parent.type.spelling,
|
||||
)
|
||||
) is not None:
|
||||
if (skip_members := skip_members_table.get(comment)) is not None:
|
||||
if member_names[i] in skip_members:
|
||||
suppress_warning = True
|
||||
|
||||
if not suppress_warning:
|
||||
result[i] = "Incorrect! found \"{:s}\" expected \"{:s}\"".format(
|
||||
comment, member_names[i])
|
||||
count_invalid += 1
|
||||
else:
|
||||
result[i] = "No comment for \"{:s}\"".format(member_names[i])
|
||||
if count_found == 0 and count_invalid == 0:
|
||||
# No comments used, skip this as not all declaration use this comment style.
|
||||
output.append(
|
||||
"NONE: {:s} {:s}".format(
|
||||
clang_source_location_as_str(node.location),
|
||||
node.type.spelling,
|
||||
)
|
||||
)
|
||||
elif count_found != total:
|
||||
for i in range(total):
|
||||
if result[i]:
|
||||
output.append(
|
||||
"FAIL: {:s} {:s}".format(
|
||||
clang_source_location_as_str(children[i].location),
|
||||
result[i],
|
||||
)
|
||||
)
|
||||
else:
|
||||
output.append(
|
||||
"OK: {:s} {:s}".format(
|
||||
clang_source_location_as_str(node.location),
|
||||
node.type.spelling,
|
||||
)
|
||||
)
|
||||
|
||||
for node_child in node.get_children():
|
||||
clang_checkers.struct_comments._struct_check_comments_recursive(
|
||||
filepath, file_data,
|
||||
node_child, node, level + 1,
|
||||
struct_decl_map, struct_type_map, output,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def check_source(
|
||||
filepath: str,
|
||||
file_data: bytes,
|
||||
tu: ClangTranslationUnit,
|
||||
_shared_check_data: Any) -> List[str]:
|
||||
output: List[str] = []
|
||||
|
||||
struct_decl_map: Dict[str, Any] = {}
|
||||
struct_type_map: Dict[str, str] = {}
|
||||
clang_checkers.struct_comments._struct_check_comments_recursive(
|
||||
filepath, file_data,
|
||||
tu.cursor, None, 0,
|
||||
struct_decl_map, struct_type_map, output,
|
||||
)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checker Class Access
|
||||
|
||||
def check_function_get_all() -> List[str]:
|
||||
checkers = []
|
||||
for name in dir(clang_checkers):
|
||||
value = getattr(clang_checkers, name)
|
||||
if isinstance(value, type) and issubclass(value, ClangChecker):
|
||||
checkers.append(name)
|
||||
checkers.sort()
|
||||
return checkers
|
||||
|
||||
|
||||
def check_class_from_id(name: str) -> Type[ClangChecker]:
|
||||
result = getattr(clang_checkers, name)
|
||||
assert issubclass(result, ClangChecker)
|
||||
# MYPY 0.812 doesn't recognize the assert above.
|
||||
return result # type: ignore
|
||||
|
||||
|
||||
def check_docstring_from_id(name: str) -> str:
|
||||
from textwrap import dedent
|
||||
result = getattr(clang_checkers, name).__doc__
|
||||
return dedent(result or '').strip('\n') + '\n'
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generic Clang Checker
|
||||
|
||||
def check_source_file(
|
||||
filepath: str,
|
||||
args: Sequence[str],
|
||||
check_ids: Sequence[str],
|
||||
shared_check_data_foreach_check: Sequence[Any],
|
||||
) -> str:
|
||||
index = clang.cindex.Index.create()
|
||||
try:
|
||||
tu = index.parse(filepath, args)
|
||||
except clang.cindex.TranslationUnitLoadError as ex:
|
||||
return "PARSE_ERROR: {:s} {!r}".format(filepath, ex)
|
||||
|
||||
with open(filepath, "rb") as fh:
|
||||
file_data = fh.read()
|
||||
|
||||
output: List[str] = []
|
||||
|
||||
# we don't really care what we are looking at, just scan entire file for
|
||||
# function calls.
|
||||
for check, shared_check_data in zip(check_ids, shared_check_data_foreach_check):
|
||||
cls = check_class_from_id(check)
|
||||
output.extend(cls.check_source(filepath, file_data, tu, shared_check_data))
|
||||
|
||||
if not output:
|
||||
return ""
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
def check_source_file_for_imap(args: Tuple[str, Sequence[str], Sequence[str], Sequence[Any]]) -> str:
|
||||
return check_source_file(*args)
|
||||
|
||||
|
||||
def source_info_filter(
|
||||
source_info: List[Tuple[str, List[str], List[str]]],
|
||||
regex_list: Sequence[re.Pattern[str]],
|
||||
) -> List[Tuple[str, List[str], List[str]]]:
|
||||
source_dir = project_source_info.SOURCE_DIR
|
||||
if not source_dir.endswith(os.sep):
|
||||
source_dir += os.sep
|
||||
source_info_result = []
|
||||
for item in source_info:
|
||||
filepath_source = item[0]
|
||||
if filepath_source.startswith(source_dir):
|
||||
filepath_source_relative = filepath_source[len(source_dir):]
|
||||
if filepath_source_relative in CHECKER_EXCLUDE_SOURCE_FILES:
|
||||
CHECKER_EXCLUDE_SOURCE_FILES.remove(filepath_source_relative)
|
||||
continue
|
||||
if filepath_source_relative.startswith("intern" + os.sep + "ghost"):
|
||||
pass
|
||||
elif filepath_source_relative.startswith("source" + os.sep):
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
|
||||
has_match = False
|
||||
for regex in regex_list:
|
||||
if regex.match(filepath_source_relative) is not None:
|
||||
has_match = True
|
||||
if not has_match:
|
||||
continue
|
||||
|
||||
source_info_result.append(item)
|
||||
|
||||
if CHECKER_EXCLUDE_SOURCE_FILES:
|
||||
sys.stderr.write(
|
||||
"Error: exclude file(s) are missing: {!r}\n".format((list(sorted(CHECKER_EXCLUDE_SOURCE_FILES))))
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
return source_info_result
|
||||
|
||||
|
||||
def run_checks_on_project(
|
||||
check_ids: Sequence[str],
|
||||
regex_list: Sequence[re.Pattern[str]],
|
||||
) -> None:
|
||||
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
|
||||
source_defines = project_source_info.build_defines_as_args()
|
||||
|
||||
# Apply exclusion.
|
||||
source_info = source_info_filter(source_info, regex_list)
|
||||
|
||||
shared_check_data_foreach_check = [
|
||||
check_class_from_id(check).setup() for check in check_ids
|
||||
]
|
||||
|
||||
all_args = []
|
||||
index = 0
|
||||
for filepath_source, inc_dirs, defs in source_info[index:]:
|
||||
args = (
|
||||
[("-I" + i) for i in inc_dirs] +
|
||||
[("-D" + d) for d in defs] +
|
||||
source_defines
|
||||
)
|
||||
|
||||
all_args.append((filepath_source, args, check_ids, shared_check_data_foreach_check))
|
||||
|
||||
import multiprocessing
|
||||
|
||||
if USE_MULTIPROCESS:
|
||||
job_total = multiprocessing.cpu_count() + 1
|
||||
with multiprocessing.Pool(processes=job_total) as pool:
|
||||
# No `istarmap`, use an intermediate function.
|
||||
for result in pool.imap(check_source_file_for_imap, all_args):
|
||||
if result:
|
||||
print(result)
|
||||
else:
|
||||
for (filepath_source, args, _check_ids, shared_check_data_foreach_check) in all_args:
|
||||
result = check_source_file(filepath_source, args, check_ids, shared_check_data_foreach_check)
|
||||
if result:
|
||||
print(result)
|
||||
|
||||
for (check, shared_check_data) in zip(check_ids, shared_check_data_foreach_check):
|
||||
check_class_from_id(check).teardown(shared_check_data)
|
||||
|
||||
|
||||
def create_parser(checkers_all: Sequence[str]) -> argparse.ArgumentParser:
|
||||
from textwrap import indent
|
||||
|
||||
# Create doc-string for checks.
|
||||
checks_all_docs = []
|
||||
for checker in checkers_all:
|
||||
# `%` -> `%%` is needed for `--help` not to interpret these as formatting arguments.
|
||||
checks_all_docs.append(
|
||||
" %s\n%s" % (
|
||||
checker,
|
||||
indent(check_docstring_from_id(checker).replace("%", "%%"), ' '),
|
||||
)
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--match",
|
||||
nargs='+',
|
||||
required=True,
|
||||
metavar="REGEX",
|
||||
help="Match file paths against this expression",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--checks",
|
||||
dest="checks",
|
||||
help=(
|
||||
"Specify the check presets to run.\n\n" +
|
||||
"\n".join(checks_all_docs) + "\n"
|
||||
"Multiple checkers may be passed at once (comma separated, no spaces)."),
|
||||
required=True,
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main Function
|
||||
|
||||
def main() -> int:
|
||||
checkers_all = check_function_get_all()
|
||||
parser = create_parser(checkers_all)
|
||||
args = parser.parse_args()
|
||||
|
||||
regex_list = []
|
||||
|
||||
for expr in args.match:
|
||||
try:
|
||||
regex_list.append(re.compile(expr))
|
||||
except Exception as ex:
|
||||
print("Error in expression: \"{:s}\"\n {!r}".format(expr, ex))
|
||||
return 1
|
||||
|
||||
run_checks_on_project(args.checks.split(','), regex_list)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -982,15 +982,15 @@ static PyMethodDef methods[] = {
|
||||
};
|
||||
|
||||
static struct PyModuleDef module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_cycles",
|
||||
"Blender cycles render integration",
|
||||
-1,
|
||||
methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "_cycles",
|
||||
/*m_doc*/ "Blender cycles render integration",
|
||||
/*m_size*/ -1,
|
||||
/*m_methods*/ methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@ -120,7 +120,7 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
||||
}
|
||||
|
||||
if (device_vendor == METAL_GPU_APPLE) {
|
||||
/* Set kernel_specialization_level based on user prefs. */
|
||||
/* Set kernel_specialization_level based on user preferences. */
|
||||
switch (info.kernel_optimization_level) {
|
||||
case KERNEL_OPTIMIZATION_LEVEL_OFF:
|
||||
kernel_specialization_level = PSO_GENERIC;
|
||||
|
@ -48,7 +48,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
|
||||
if (r_sq == 0) {
|
||||
/* Use intensity instead of radiance for point light. */
|
||||
ls->eval_fac /= sqr(ls->t);
|
||||
/* `ls->Ng` is not well-defined for point light, so use the incoming direction instead. */
|
||||
/* `ls->Ng` is not well-defined for point light, so use the incoming direction instead. */
|
||||
ls->Ng = -ls->D;
|
||||
}
|
||||
else {
|
||||
|
@ -33,11 +33,16 @@ shader node_normal_map(float Strength = 1.0,
|
||||
if (getattribute(attr_name, tangent) && getattribute(attr_sign_name, tangent_sign) &&
|
||||
(!is_smooth || getattribute("geom:normal_map_normal", ninterp)))
|
||||
{
|
||||
// apply normal map
|
||||
/* apply normal map */
|
||||
vector B = tangent_sign * cross(ninterp, tangent);
|
||||
|
||||
/* apply strength */
|
||||
mcolor[0] *= Strength;
|
||||
mcolor[1] *= Strength;
|
||||
|
||||
Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp);
|
||||
|
||||
// transform to world space
|
||||
/* transform to world space */
|
||||
Normal = normalize(transform("object", "world", Normal));
|
||||
}
|
||||
else {
|
||||
@ -70,6 +75,6 @@ shader node_normal_map(float Strength = 1.0,
|
||||
Normal = -Normal;
|
||||
}
|
||||
|
||||
if (Strength != 1.0)
|
||||
if (Strength != 1.0 && space != "tangent")
|
||||
Normal = normalize(N + (Normal - N) * max(Strength, 0.0));
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
|
||||
|
||||
bool is_backfacing = (sd->flag & SD_BACKFACING) != 0;
|
||||
float3 N;
|
||||
|
||||
float strength = stack_load_float(stack, strength_offset);
|
||||
if (space == NODE_NORMAL_MAP_TANGENT) {
|
||||
/* tangent space */
|
||||
if (sd->object == OBJECT_NONE || (sd->type & PRIMITIVE_TRIANGLE) == 0) {
|
||||
@ -313,6 +313,9 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
|
||||
|
||||
object_inverse_normal_transform(kg, sd, &normal);
|
||||
}
|
||||
/* Apply strength in the tangent case. */
|
||||
color.x *= strength;
|
||||
color.y *= strength;
|
||||
|
||||
/* apply normal map */
|
||||
float3 B = sign * cross(normal, tangent);
|
||||
@ -335,6 +338,11 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
|
||||
object_normal_transform(kg, sd, &N);
|
||||
else
|
||||
N = safe_normalize(N);
|
||||
/* Apply strength in all but tangent space. */
|
||||
if (strength != 1.0f) {
|
||||
strength = max(strength, 0.0f);
|
||||
N = safe_normalize(sd->N + (N - sd->N) * strength);
|
||||
}
|
||||
}
|
||||
|
||||
/* invert normal for backfacing polygons */
|
||||
@ -342,13 +350,6 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
|
||||
N = -N;
|
||||
}
|
||||
|
||||
float strength = stack_load_float(stack, strength_offset);
|
||||
|
||||
if (strength != 1.0f) {
|
||||
strength = max(strength, 0.0f);
|
||||
N = safe_normalize(sd->N + (N - sd->N) * strength);
|
||||
}
|
||||
|
||||
if (is_zero(N)) {
|
||||
N = sd->N;
|
||||
}
|
||||
|
@ -1706,7 +1706,7 @@ enum KernelFeatureFlag : uint32_t {
|
||||
KERNEL_FEATURE_TRANSPARENT = (1U << 19U),
|
||||
|
||||
/* Use shadow catcher. */
|
||||
KERNEL_FEATURE_SHADOW_CATCHER = (1U << 29U),
|
||||
KERNEL_FEATURE_SHADOW_CATCHER = (1U << 20U),
|
||||
|
||||
/* Light render passes. */
|
||||
KERNEL_FEATURE_LIGHT_PASSES = (1U << 21U),
|
||||
|
@ -458,7 +458,7 @@ void LightTree::recursive_build(const Child child,
|
||||
if (should_split(emitters, start, middle, end, node->measure, node->light_link, split_dim)) {
|
||||
|
||||
if (split_dim != -1) {
|
||||
/* Partition the emitters between start and end based on the centroids. */
|
||||
/* Partition the emitters between start and end based on the centroids. */
|
||||
std::nth_element(emitters + start,
|
||||
emitters + middle,
|
||||
emitters + end,
|
||||
|
@ -297,7 +297,7 @@ ccl_device void math_matrix_jacobi_eigendecomposition(ccl_private float *A,
|
||||
/* Determine rotation: The rotation is characterized by its angle phi - or,
|
||||
* in the actual implementation, sin(phi) and cos(phi).
|
||||
* To find those, we first compute their ratio - that might be unstable if the angle
|
||||
* approaches 90°, so there's a fallback for that case.
|
||||
* approaches 90 degrees, so there's a fallback for that case.
|
||||
* Then, we compute sin(phi) and cos(phi) themselves. */
|
||||
float singular_diff = MAT(A, n, row, row) - MAT(A, n, col, col);
|
||||
float ratio;
|
||||
|
@ -23,7 +23,9 @@
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
#include "GHOST_ContextCGL.hh"
|
||||
#if defined(WITH_OPENGL_BACKEND) || defined(WITH_METAL_BACKEND)
|
||||
# include "GHOST_ContextCGL.hh"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.hh"
|
||||
@ -761,25 +763,42 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
|
||||
*/
|
||||
GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GPUSettings gpuSettings)
|
||||
{
|
||||
const bool debug_context = (gpuSettings.flags & GHOST_gpuDebugContext) != 0;
|
||||
|
||||
switch (gpuSettings.context_type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
if (gpuSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
|
||||
const bool debug_context = (gpuSettings.flags & GHOST_gpuDebugContext) != 0;
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 2, debug_context);
|
||||
if (!context->initializeDrawingContext()) {
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 2, debug_context);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GHOST_kDrawingContextTypeOpenGL:
|
||||
#endif
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
case GHOST_kDrawingContextTypeMetal:
|
||||
#endif
|
||||
#if defined(WITH_OPENGL_BACKEND) || defined(WITH_METAL_BACKEND)
|
||||
{
|
||||
/* TODO(fclem): Remove OpenGL support and rename context to ContextMTL */
|
||||
GHOST_Context *context = new GHOST_ContextCGL(
|
||||
false, NULL, NULL, NULL, gpuSettings.context_type);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
#endif
|
||||
|
||||
GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL, gpuSettings.context_type);
|
||||
if (context->initializeDrawingContext())
|
||||
return context;
|
||||
else
|
||||
delete context;
|
||||
|
||||
return NULL;
|
||||
default:
|
||||
/* Unsupported backend. */
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,7 +85,7 @@ class GHOST_SystemHeadless : public GHOST_System {
|
||||
{
|
||||
#ifdef __linux__
|
||||
GHOST_Context *context;
|
||||
for (int minor = 6; minor >= 0; --minor) {
|
||||
for (int minor = 6; minor >= 3; --minor) {
|
||||
context = new GHOST_ContextEGL((GHOST_System *)this,
|
||||
false,
|
||||
EGLNativeWindowType(0),
|
||||
@ -104,21 +104,6 @@ class GHOST_SystemHeadless : public GHOST_System {
|
||||
context = nullptr;
|
||||
}
|
||||
|
||||
context = new GHOST_ContextEGL((GHOST_System *)this,
|
||||
false,
|
||||
EGLNativeWindowType(0),
|
||||
EGLNativeDisplayType(EGL_DEFAULT_DISPLAY),
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
3,
|
||||
3,
|
||||
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
|
||||
EGL_OPENGL_API);
|
||||
|
||||
if (context->initializeDrawingContext() != GHOST_kSuccess) {
|
||||
delete context;
|
||||
context = nullptr;
|
||||
}
|
||||
return context;
|
||||
#else
|
||||
return nullptr;
|
||||
|
@ -127,22 +127,34 @@ uint8_t GHOST_SystemSDL::getNumDisplays() const
|
||||
return SDL_GetNumVideoDisplays();
|
||||
}
|
||||
|
||||
GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GPUSettings /*gpuSettings*/)
|
||||
GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GPUSettings gpuSettings)
|
||||
{
|
||||
GHOST_Context *context = new GHOST_ContextSDL(false,
|
||||
nullptr,
|
||||
0, /* Profile bit. */
|
||||
3,
|
||||
3,
|
||||
GHOST_OPENGL_SDL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY);
|
||||
switch (gpuSettings.context_type) {
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GHOST_kDrawingContextTypeOpenGL: {
|
||||
for (int minor = 6; minor >= 3; --minor) {
|
||||
GHOST_Context *context = new GHOST_ContextSDL(
|
||||
false,
|
||||
nullptr,
|
||||
0, /* Profile bit. */
|
||||
4,
|
||||
minor,
|
||||
GHOST_OPENGL_SDL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
/* Unsupported backend. */
|
||||
return nullptr;
|
||||
}
|
||||
delete context;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::disposeContext(GHOST_IContext *context)
|
||||
|
@ -20,7 +20,9 @@
|
||||
#include "GHOST_WindowManager.hh"
|
||||
#include "GHOST_utildefines.hh"
|
||||
|
||||
#include "GHOST_ContextEGL.hh"
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
# include "GHOST_ContextEGL.hh"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.hh"
|
||||
@ -6243,102 +6245,88 @@ void GHOST_SystemWayland::getAllDisplayDimensions(uint32_t &width, uint32_t &hei
|
||||
height = xy_max[1] - xy_min[1];
|
||||
}
|
||||
|
||||
static GHOST_Context *createOffscreenContext_impl(GHOST_SystemWayland *system,
|
||||
wl_display *wl_display,
|
||||
wl_egl_window *egl_window)
|
||||
{
|
||||
/* Caller must lock `system->server_mutex`. */
|
||||
GHOST_Context *context;
|
||||
|
||||
for (int minor = 6; minor >= 0; --minor) {
|
||||
context = new GHOST_ContextEGL(system,
|
||||
false,
|
||||
EGLNativeWindowType(egl_window),
|
||||
EGLNativeDisplayType(wl_display),
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
4,
|
||||
minor,
|
||||
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
|
||||
EGL_OPENGL_API);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
}
|
||||
|
||||
context = new GHOST_ContextEGL(system,
|
||||
false,
|
||||
EGLNativeWindowType(egl_window),
|
||||
EGLNativeDisplayType(wl_display),
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
3,
|
||||
3,
|
||||
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
|
||||
EGL_OPENGL_API);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GPUSettings gpuSettings)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
/* Create new off-screen window. */
|
||||
wl_surface *wl_surface = wl_compositor_create_surface(wl_compositor());
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
const bool debug_context = (gpuSettings.flags & GHOST_gpuDebugContext) != 0;
|
||||
|
||||
if (gpuSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false,
|
||||
GHOST_kVulkanPlatformWayland,
|
||||
0,
|
||||
NULL,
|
||||
wl_surface,
|
||||
display_->wl_display,
|
||||
1,
|
||||
2,
|
||||
debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
context->setUserData(wl_surface);
|
||||
return context;
|
||||
}
|
||||
#else
|
||||
(void)gpuSettings;
|
||||
#endif
|
||||
|
||||
wl_egl_window *egl_window = wl_surface ? wl_egl_window_create(wl_surface, 1, 1) : nullptr;
|
||||
switch (gpuSettings.context_type) {
|
||||
|
||||
GHOST_Context *context = createOffscreenContext_impl(this, display_->wl_display, egl_window);
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
/* Create new off-screen surface only for vulkan. */
|
||||
wl_surface *wl_surface = wl_compositor_create_surface(wl_compositor());
|
||||
|
||||
if (!context) {
|
||||
GHOST_PRINT("Cannot create off-screen EGL context" << std::endl);
|
||||
if (wl_surface) {
|
||||
wl_surface_destroy(wl_surface);
|
||||
GHOST_Context *context = new GHOST_ContextVK(false,
|
||||
GHOST_kVulkanPlatformWayland,
|
||||
0,
|
||||
NULL,
|
||||
wl_surface,
|
||||
display_->wl_display,
|
||||
1,
|
||||
2,
|
||||
debug_context);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
context->setUserData(wl_surface);
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
|
||||
if (wl_surface) {
|
||||
wl_surface_destroy(wl_surface);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (egl_window) {
|
||||
wl_egl_window_destroy(egl_window);
|
||||
#endif /* WITH_VULKAN_BACKEND */
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GHOST_kDrawingContextTypeOpenGL: {
|
||||
/* Create new off-screen window. */
|
||||
wl_surface *wl_surface = wl_compositor_create_surface(wl_compositor());
|
||||
wl_egl_window *egl_window = wl_surface ? wl_egl_window_create(wl_surface, 1, 1) : nullptr;
|
||||
|
||||
for (int minor = 6; minor >= 3; --minor) {
|
||||
/* Caller must lock `system->server_mutex`. */
|
||||
GHOST_Context *context = new GHOST_ContextEGL(this,
|
||||
false,
|
||||
EGLNativeWindowType(egl_window),
|
||||
EGLNativeDisplayType(display_->wl_display),
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
4,
|
||||
minor,
|
||||
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
|
||||
EGL_OPENGL_API);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
wl_surface_set_user_data(wl_surface, egl_window);
|
||||
context->setUserData(wl_surface);
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
}
|
||||
|
||||
GHOST_PRINT("Cannot create off-screen EGL context" << std::endl);
|
||||
if (wl_surface) {
|
||||
wl_surface_destroy(wl_surface);
|
||||
}
|
||||
if (egl_window) {
|
||||
wl_egl_window_destroy(egl_window);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
#endif /* WITH_OPENGL_BACKEND */
|
||||
|
||||
default:
|
||||
/* Unsupported backend. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wl_surface_set_user_data(wl_surface, egl_window);
|
||||
context->setUserData(wl_surface);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context)
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include "GHOST_SystemWin32.hh"
|
||||
#include "GHOST_ContextD3D.hh"
|
||||
#include "GHOST_EventDragnDrop.hh"
|
||||
#include "GHOST_EventTrackpad.hh"
|
||||
|
||||
@ -40,7 +39,10 @@
|
||||
#include "GHOST_WindowManager.hh"
|
||||
#include "GHOST_WindowWin32.hh"
|
||||
|
||||
#include "GHOST_ContextWGL.hh"
|
||||
#include "GHOST_ContextD3D.hh"
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
# include "GHOST_ContextWGL.hh"
|
||||
#endif
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.hh"
|
||||
#endif
|
||||
@ -268,77 +270,64 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GPUSettings gpuS
|
||||
{
|
||||
const bool debug_context = (gpuSettings.flags & GHOST_gpuDebugContext) != 0;
|
||||
|
||||
GHOST_Context *context = nullptr;
|
||||
|
||||
switch (gpuSettings.context_type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
/* Vulkan does not need a window. */
|
||||
if (gpuSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
|
||||
context = new GHOST_ContextVK(false, (HWND)0, 1, 2, debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, (HWND)0, 1, 2, debug_context);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return nullptr;
|
||||
}
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
#endif
|
||||
|
||||
HWND wnd = CreateWindowA("STATIC",
|
||||
"BlenderGLEW",
|
||||
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
||||
0,
|
||||
0,
|
||||
64,
|
||||
64,
|
||||
NULL,
|
||||
NULL,
|
||||
GetModuleHandle(NULL),
|
||||
NULL);
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GHOST_kDrawingContextTypeOpenGL: {
|
||||
|
||||
HDC mHDC = GetDC(wnd);
|
||||
HDC prev_hdc = wglGetCurrentDC();
|
||||
HGLRC prev_context = wglGetCurrentContext();
|
||||
/* OpenGL needs a dummy window to create a context on windows. */
|
||||
HWND wnd = CreateWindowA("STATIC",
|
||||
"BlenderGLEW",
|
||||
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
||||
0,
|
||||
0,
|
||||
64,
|
||||
64,
|
||||
NULL,
|
||||
NULL,
|
||||
GetModuleHandle(NULL),
|
||||
NULL);
|
||||
|
||||
for (int minor = 5; minor >= 0; --minor) {
|
||||
context = new GHOST_ContextWGL(false,
|
||||
true,
|
||||
wnd,
|
||||
mHDC,
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
4,
|
||||
minor,
|
||||
(debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
|
||||
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
|
||||
HDC mHDC = GetDC(wnd);
|
||||
HDC prev_hdc = wglGetCurrentDC();
|
||||
HGLRC prev_context = wglGetCurrentContext();
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
goto finished;
|
||||
}
|
||||
else {
|
||||
delete context;
|
||||
for (int minor = 6; minor >= 3; --minor) {
|
||||
GHOST_Context *context = new GHOST_ContextWGL(
|
||||
false,
|
||||
true,
|
||||
wnd,
|
||||
mHDC,
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
4,
|
||||
minor,
|
||||
(debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
|
||||
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
wglMakeCurrent(prev_hdc, prev_context);
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
}
|
||||
wglMakeCurrent(prev_hdc, prev_context);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
/* Unsupported backend. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
context = new GHOST_ContextWGL(false,
|
||||
true,
|
||||
wnd,
|
||||
mHDC,
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
3,
|
||||
3,
|
||||
(debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
|
||||
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
goto finished;
|
||||
}
|
||||
else {
|
||||
delete context;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
finished:
|
||||
wglMakeCurrent(prev_hdc, prev_context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -360,8 +349,6 @@ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
|
||||
*/
|
||||
GHOST_ContextD3D *GHOST_SystemWin32::createOffscreenContextD3D()
|
||||
{
|
||||
GHOST_ContextD3D *context;
|
||||
|
||||
HWND wnd = CreateWindowA("STATIC",
|
||||
"Blender XR",
|
||||
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
||||
@ -374,13 +361,12 @@ GHOST_ContextD3D *GHOST_SystemWin32::createOffscreenContextD3D()
|
||||
GetModuleHandle(NULL),
|
||||
NULL);
|
||||
|
||||
context = new GHOST_ContextD3D(false, wnd);
|
||||
if (context->initializeDrawingContext() == GHOST_kFailure) {
|
||||
delete context;
|
||||
context = nullptr;
|
||||
GHOST_ContextD3D *context = new GHOST_ContextD3D(false, wnd);
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
return context;
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWin32::disposeContextD3D(GHOST_ContextD3D *context)
|
||||
@ -465,8 +451,8 @@ GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y)
|
||||
GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
|
||||
{
|
||||
/* `GetAsyncKeyState` returns the current interrupt-level state of the hardware, which is needed
|
||||
* when passing key states to a newly-activated window - #40059. Alterative `GetKeyState` only
|
||||
* returns the state as processed by the thread's message queue. */
|
||||
* when passing key states to a newly-activated window - #40059. Alternative `GetKeyState` only
|
||||
* returns the state as processed by the thread's message queue. */
|
||||
bool down = HIBYTE(::GetAsyncKeyState(VK_LSHIFT)) != 0;
|
||||
keys.set(GHOST_kModifierKeyLeftShift, down);
|
||||
down = HIBYTE(::GetAsyncKeyState(VK_RSHIFT)) != 0;
|
||||
|
@ -35,8 +35,10 @@
|
||||
|
||||
#include "GHOST_Debug.hh"
|
||||
|
||||
#include "GHOST_ContextEGL.hh"
|
||||
#include "GHOST_ContextGLX.hh"
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
# include "GHOST_ContextEGL.hh"
|
||||
# include "GHOST_ContextGLX.hh"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.hh"
|
||||
@ -350,112 +352,48 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
|
||||
return window;
|
||||
}
|
||||
|
||||
#ifdef USE_EGL
|
||||
static GHOST_Context *create_egl_context(
|
||||
GHOST_SystemX11 *system, Display *display, bool debug_context, int ver_major, int ver_minor)
|
||||
{
|
||||
GHOST_Context *context;
|
||||
context = new GHOST_ContextEGL(system,
|
||||
false,
|
||||
EGLNativeWindowType(nullptr),
|
||||
EGLNativeDisplayType(display),
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
ver_major,
|
||||
ver_minor,
|
||||
GHOST_OPENGL_EGL_CONTEXT_FLAGS |
|
||||
(debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
|
||||
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
|
||||
EGL_OPENGL_API);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|