UI: Console Text Operations #108626

Manually merged
Campbell Barton merged 13 commits from Harley/blender:ConsoleSelect into main 2023-09-22 05:42:35 +02:00
662 changed files with 20956 additions and 18423 deletions
Showing only changes of commit aa5459d5f3 - Show all commits

View File

@ -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)" ; \

View 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())

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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),

View File

@ -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,

View File

@ -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;

View File

@ -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;
}
}
/**

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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;
}