Anim: run bezier handle calculation in parallel #119388
|
@ -529,9 +529,9 @@ set(MATERIALX_HASH fad8f4e19305fb2ee920cbff638f3560)
|
|||
set(MATERIALX_HASH_TYPE MD5)
|
||||
set(MATERIALX_FILE materialx-v${MATERIALX_VERSION}.tar.gz)
|
||||
|
||||
set(OIDN_VERSION 2.2.1)
|
||||
set(OIDN_VERSION 2.2.2)
|
||||
set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.tar.gz)
|
||||
set(OIDN_HASH a1c5299b2b640a0e0569afcf405c82bf)
|
||||
set(OIDN_HASH 40c04b0371334ab863230e99a587fd59)
|
||||
set(OIDN_HASH_TYPE MD5)
|
||||
set(OIDN_FILE oidn-${OIDN_VERSION}.src.tar.gz)
|
||||
|
||||
|
|
|
@ -176,7 +176,6 @@ def main() -> None:
|
|||
# Support version without a minor version "3" (add zero).
|
||||
tuple((0, 0, 0))
|
||||
)
|
||||
python_version_str = "%d.%d" % python_version_number[:2]
|
||||
|
||||
# Get Blender version.
|
||||
blender_version_str = str(make_utils.parse_blender_version())
|
||||
|
|
|
@ -9,4 +9,5 @@ requests==2.31.0
|
|||
|
||||
# Only needed to match the theme used for the official documentation.
|
||||
# Without this theme, the default theme will be used.
|
||||
sphinx_rtd_theme==1.3.0rc1
|
||||
furo==2024.1.29
|
||||
sphinx-basic-ng==1.0.0b2
|
||||
|
|
|
@ -1929,22 +1929,30 @@ def write_sphinx_conf_py(basepath):
|
|||
# The theme 'sphinx_rtd_theme' is no longer distributed with sphinx by default, only use when available.
|
||||
fw(r"""
|
||||
try:
|
||||
__import__('sphinx_rtd_theme')
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
import furo
|
||||
html_theme = "furo"
|
||||
del furo
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
""")
|
||||
if html_theme == "furo":
|
||||
html_theme_options = {
|
||||
"light_css_variables": {
|
||||
"color-brand-primary": "#265787",
|
||||
"color-brand-content": "#265787",
|
||||
},
|
||||
}
|
||||
|
||||
fw("if html_theme == 'sphinx_rtd_theme':\n")
|
||||
fw(" html_theme_options = {\n")
|
||||
fw(" 'display_version': False,\n")
|
||||
# fw(" 'analytics_id': '',\n")
|
||||
# fw(" 'collapse_navigation': True,\n")
|
||||
fw(" 'sticky_navigation': False,\n")
|
||||
fw(" 'navigation_depth': 1,\n")
|
||||
fw(" 'includehidden': False,\n")
|
||||
# fw(" 'titles_only': False\n")
|
||||
fw(" }\n\n")
|
||||
html_sidebars = {
|
||||
"**": [
|
||||
"sidebar/brand.html",
|
||||
"sidebar/search.html",
|
||||
"sidebar/scroll-start.html",
|
||||
"sidebar/navigation.html",
|
||||
"sidebar/scroll-end.html",
|
||||
# "sidebar/variant-selector.html",
|
||||
]
|
||||
}
|
||||
""")
|
||||
|
||||
# not helpful since the source is generated, adds to upload size.
|
||||
fw("html_copy_source = False\n")
|
||||
|
@ -1961,7 +1969,7 @@ except ModuleNotFoundError:
|
|||
fw("html_logo = 'static/blender_logo.svg'\n")
|
||||
# Disable default `last_updated` value, since this is the date of doc generation, not the one of the source commit.
|
||||
fw("html_last_updated_fmt = None\n\n")
|
||||
fw("if html_theme == 'sphinx_rtd_theme':\n")
|
||||
fw("if html_theme == 'furo':\n")
|
||||
fw(" html_css_files = ['css/version_switch.css']\n")
|
||||
fw(" html_js_files = ['js/version_switch.js']\n")
|
||||
|
||||
|
|
|
@ -1,19 +1,323 @@
|
|||
/* Hide home icon in search area */
|
||||
.wy-side-nav-search > a:hover {background: none; opacity: 0.9}
|
||||
.wy-side-nav-search > a.icon::before {content: none}
|
||||
/*
|
||||
* This stylesheet is applied after the theme's default one,
|
||||
* and thus any overrides or additions can be added here.
|
||||
*
|
||||
* More info:
|
||||
* https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.add_css_file
|
||||
*/
|
||||
|
||||
.wy-nav-content {
|
||||
max-width: 1000px !important;
|
||||
body {
|
||||
--sidebar-caption-font-size: var(--font-size--normal);
|
||||
--toc-title-font-size: var(--font-size--normal);
|
||||
--toc-font-size: var(--sidebar-item-font-size);
|
||||
--admonition-font-size: var(--font-size--normal);
|
||||
--admonition-title-font-size: var(--font-size--normal);
|
||||
--color-api-name: #e87d0d;
|
||||
}
|
||||
|
||||
/* Fix long titles on mobile */
|
||||
h1, h2, h3, h4, h5, h6 {word-break: break-all}
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin-top: 1.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Temp fix for https://github.com/readthedocs/sphinx_rtd_theme/pull/1109 */
|
||||
.hlist tr {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.hlist td {margin-right: auto}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
.rubric {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1.125em;
|
||||
}
|
||||
|
||||
/* Reduce the margins on top/bottom of horizontal lines. */
|
||||
hr.docutils {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
|
||||
/* Slightly decrease text to make the text fit on one line */
|
||||
.sidebar-brand-text {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.toctree-checkbox~label .icon svg {
|
||||
transition: transform 0.25s ease-out;
|
||||
}
|
||||
|
||||
/* Add more visual weight to definition terms */
|
||||
dl dt {
|
||||
font-weight: bold !important
|
||||
}
|
||||
|
||||
/* Fixes to field list, see #104636 */
|
||||
dl.field-list {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(80%, 95%);
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* TABLE & FIGURE */
|
||||
|
||||
/* Cell's vertical align. */
|
||||
/* use "valign" class for middle align */
|
||||
table.docutils:not(.valign) td {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* Decrease whitespace above figure and add it below */
|
||||
figure {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
margin-bottom: 0.5rem !important;
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* End TABLE & FIGURE. */
|
||||
|
||||
/* Force admonition to span the full width if close to a figure */
|
||||
.admonition {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* Use secondary font color for caption text */
|
||||
figcaption,
|
||||
caption {
|
||||
color: var(--color-foreground-secondary);
|
||||
font-size: var(--font-size--small)
|
||||
}
|
||||
|
||||
/* A bit hacky, revert the themes styling of kbd */
|
||||
kbd:not(.compound) {
|
||||
all: revert;
|
||||
}
|
||||
|
||||
/* Only style parent kbd elements instead of the individual children */
|
||||
:not(dl.option-list)> :not(kbd):not(kbd)>kbd,
|
||||
.menuselection {
|
||||
background-color: var(--color-background-secondary);
|
||||
border: 1px solid var(--color-foreground-border);
|
||||
border-radius: .2rem;
|
||||
box-shadow: 0 .0625rem 0 rgba(0, 0, 0, .2), inset 0 0 0 .125rem var(--color-background-secondary);
|
||||
color: var(--color-foreground-primary);
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0 .2rem;
|
||||
}
|
||||
|
||||
.highlight .nc,
|
||||
.highlight .nn,
|
||||
.highlight .gu {
|
||||
text-decoration-line: none !important;
|
||||
}
|
||||
|
||||
.caption .menuselection {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Quotes for Fig. "link". */
|
||||
a[href^="#fig-"]::before {
|
||||
content: "\201c";
|
||||
}
|
||||
|
||||
a[href^="#fig-"]::after {
|
||||
content: "\201d";
|
||||
}
|
||||
|
||||
/* Mark external links. */
|
||||
a.external {
|
||||
filter: brightness(150%);
|
||||
}
|
||||
|
||||
/* List blender.org as internal. */
|
||||
.external[href^="https://www.blender.org"],
|
||||
.external[href^="https://docs.blender.org"],
|
||||
.external[href^="https://projects.blender.org"],
|
||||
.external[href^="https://builder.blender.org"],
|
||||
.external[href^="https://code.blender.org"],
|
||||
.external[href^="https://translate.blender.org"],
|
||||
.external[href^="https://fund.blender.org"],
|
||||
.external[href^="blender_manual_html.zip"],
|
||||
.external[href^="blender_manual_epub.zip"],
|
||||
.external[href^="https://archive.blender.org"] {
|
||||
filter: revert;
|
||||
}
|
||||
|
||||
/* ".. container::" lead, block text float around image. */
|
||||
.lead {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Start reference admonition. */
|
||||
.admonition.refbox {
|
||||
border-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
.admonition.refbox>.admonition-title {
|
||||
background-color: rgba(50, 50, 50, 0.2);
|
||||
border-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
.admonition.refbox>.admonition-title::before {
|
||||
background-color: var(--color-content-foreground);
|
||||
}
|
||||
|
||||
/* 'refbox' field. */
|
||||
.refbox .field-list .field-name,
|
||||
.refbox .field-list .field-body {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.refbox dl dt {
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
/* End reference admonition. */
|
||||
|
||||
/* Applied on main index:sections. */
|
||||
|
||||
.global-index-toc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Start section cards. */
|
||||
.toc-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
grid-gap: 20px;
|
||||
list-style-type: none;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: .3em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.card div.figure,
|
||||
.card figure {
|
||||
margin-bottom: 0px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card img {
|
||||
border-top-left-radius: .3em;
|
||||
border-top-right-radius: .3em;
|
||||
}
|
||||
|
||||
.card dl {
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
.card dl dt>a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card dl dt a em,
|
||||
.card dl dt a span {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.card dl dt {
|
||||
padding: 0px 15px 0px !important
|
||||
}
|
||||
|
||||
.card dl dd {
|
||||
padding: 0px 15px 5px 15px;
|
||||
font-style: normal;
|
||||
margin: 0px;
|
||||
color: var(--color-foreground-secondary);
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 .2rem .5rem rgba(0, 0, 0, .05), 0 0 .0625rem rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
#getting-started .card {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* End section cards. */
|
||||
|
||||
/* Start custom toctree. */
|
||||
/* Indent all lines following the first. */
|
||||
.toctree-wrapper * a {
|
||||
display: block;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
|
||||
.toctree-wrapper ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* Underline provided by nested ul (not li). */
|
||||
.toctree-wrapper * ul {
|
||||
margin-bottom: 1rem !important;
|
||||
border-top: solid var(--color-background-border) 1px;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
/* End custom toctree. */
|
||||
|
||||
/* Start footer contribute link */
|
||||
.footer-contribute {
|
||||
display: block;
|
||||
font-size: var(--font-size--small);
|
||||
}
|
||||
|
||||
.bottom-of-page {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.footer-contribute ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 1rem
|
||||
}
|
||||
|
||||
.footer-contribute li {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.footer-contribute {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* End footer contribute link */
|
|
@ -1,95 +1,98 @@
|
|||
/* Override RTD theme */
|
||||
.rst-versions {
|
||||
display: none;
|
||||
border-top: 0px;
|
||||
overflow: visible;
|
||||
}
|
||||
.version-btn.vdeact {
|
||||
cursor: default;
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
.version-btn.vdeact::after {
|
||||
content: "";
|
||||
}
|
||||
#versionwrap {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
padding-top: 2px;
|
||||
font-size: 90%;
|
||||
padding-left: 0;
|
||||
font-size: var(--sidebar-item-font-size);
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#versionwrap>ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#versionwrap>li {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.version-btn {
|
||||
display: inline-block;
|
||||
background-color: #272525;
|
||||
width: 140px;
|
||||
background-color: var(--color-sidebar-background);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 3px 10px;
|
||||
margin: 0px 5px 4px;
|
||||
vertical-align: middle;
|
||||
color: #27AE60;
|
||||
border: solid 1px #444444;
|
||||
color: var(--color-link);
|
||||
border: solid 1px var(--color-sidebar-background-border);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
z-index: 400;
|
||||
transition: border-color 0.4s;
|
||||
}
|
||||
.version-btn::after {
|
||||
content:"\f0d8";
|
||||
display: inline;
|
||||
font: normal normal normal 16px/1 FontAwesome;
|
||||
color: #8d8c8c;
|
||||
vertical-align: top;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.version-btn-open::after {
|
||||
color: gray;
|
||||
}
|
||||
.version-btn:hover, .version-btn:focus {
|
||||
|
||||
.version-btn:hover,
|
||||
.version-btn:focus {
|
||||
border-color: #525252;
|
||||
}
|
||||
|
||||
.version-btn-open {
|
||||
color: gray;
|
||||
border: solid 1px gray;
|
||||
border: solid 1px var(--color-sidebar-background-border);
|
||||
}
|
||||
|
||||
.version-btn.wait {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.version-btn.disabled {
|
||||
cursor: not-allowed;
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
.version-dialog {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 28px;
|
||||
width: 140px;
|
||||
width: 50%;
|
||||
margin: 0 5px;
|
||||
padding-bottom: 4px;
|
||||
background-color: #0003;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 6px #000C;
|
||||
z-index: 999;
|
||||
max-height: calc(100vh - 30px);
|
||||
overflow-x: clip;
|
||||
overflow-y: auto;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.version-title {
|
||||
padding: 5px;
|
||||
color: black;
|
||||
color: var(--color-content-foreground);
|
||||
text-align: center;
|
||||
font-size: 102%;
|
||||
background-color: #27ae60;
|
||||
border-bottom: solid 1.5px #444;
|
||||
font-weight: 700;
|
||||
background-color: var(--color-brand-primary);
|
||||
border-bottom: solid 1.5px var(--color-sidebar-background-border);
|
||||
}
|
||||
|
||||
.version-list {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 4px;
|
||||
text-align: center;
|
||||
background-color: #000C;
|
||||
border: solid 1px gray;
|
||||
border: solid 1px var(--color-sidebar-background-border);
|
||||
border-radius: 0px 0px 3px 3px;
|
||||
}
|
||||
.version-list a, .version-list span, .version-list li {
|
||||
|
||||
.version-list a,
|
||||
.version-list span,
|
||||
.version-list li {
|
||||
position: relative;
|
||||
display: block;
|
||||
font-size: 98%;
|
||||
|
@ -97,32 +100,21 @@
|
|||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 4px 0px;
|
||||
color: #404040;
|
||||
color: var(--color-sidebar-link-text);
|
||||
}
|
||||
|
||||
.version-list li {
|
||||
background-color: #ede9e9;
|
||||
color: #404040;
|
||||
background-color: var(--color-sidebar-background);
|
||||
color: var(--color-sidebar-link-text);
|
||||
padding: 1px;
|
||||
}
|
||||
.version-list li:hover, .version-list li a:focus {
|
||||
background-color: #b9cfda;
|
||||
|
||||
.version-list li:hover,
|
||||
.version-list li a:focus {
|
||||
background-color: var(--color-background-hover);
|
||||
}
|
||||
.version-list li.selected, .version-list li.selected:hover {
|
||||
background-color: #8d8c8c;
|
||||
}
|
||||
.version-list li.selected span {
|
||||
cursor: default;
|
||||
outline-color: red;
|
||||
}
|
||||
.version-arrow {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
left: 50%;
|
||||
bottom: 4px;
|
||||
margin-left: -4px;
|
||||
transform: rotate(225deg);
|
||||
background: #ede9e9;
|
||||
border: 1px solid gray;
|
||||
border-width: 1px 0 0 1px;
|
||||
|
||||
.version-list li.selected {
|
||||
background: var(--color-sidebar-item-background--current);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
|
|
@ -1,63 +1,60 @@
|
|||
(function() { // switch: v1.2
|
||||
(function() { // switch: v1.4
|
||||
"use strict";
|
||||
|
||||
var versionsFileUrl = "https://docs.blender.org/PROD/versions.json"
|
||||
|
||||
var all_versions;
|
||||
|
||||
var Popover = function() {
|
||||
function Popover(id)
|
||||
class Popover {
|
||||
constructor(id)
|
||||
{
|
||||
this.isOpen = false;
|
||||
this.type = (id === "version-popover");
|
||||
this.$btn = $('#' + id);
|
||||
this.$dialog = this.$btn.next();
|
||||
this.$list = this.$dialog.children("ul");
|
||||
this.btn = document.querySelector('#' + id);
|
||||
this.dialog = this.btn.nextElementSibling;
|
||||
this.list = this.dialog.querySelector("ul");
|
||||
this.sel = null;
|
||||
this.beforeInit();
|
||||
}
|
||||
|
||||
Popover.prototype = {
|
||||
beforeInit : function() {
|
||||
var that = this;
|
||||
this.$btn.on("click", function(e) {
|
||||
const that = this;
|
||||
this.btnClickHandler = function(e) {
|
||||
that.init();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
this.btnKeyHandler = function(e) {
|
||||
if (that.btnKeyFilter(e)) {
|
||||
that.init();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
this.$btn.on("keydown", function(e) {
|
||||
if (that.btnKeyFilter(e)) {
|
||||
that.init();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
},
|
||||
init : function() {
|
||||
this.$btn.off("click");
|
||||
this.$btn.off("keydown");
|
||||
}
|
||||
};
|
||||
this.btn.addEventListener("click", this.btnClickHandler);
|
||||
this.btn.addEventListener("keydown", this.btnKeyHandler);
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
this.btn.removeEventListener("click", this.btnClickHandler);
|
||||
this.btn.removeEventListener("keydown", this.btnKeyHandler);
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
if (all_versions === undefined) {
|
||||
this.$btn.addClass("wait");
|
||||
this.loadVL(this);
|
||||
this.btn.classList.add("wait");
|
||||
fetch(versionsFileUrl)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
all_versions = data;
|
||||
resolve();
|
||||
})
|
||||
.catch(() => {
|
||||
console.error("Version Switch Error: versions.json could not be loaded.");
|
||||
this.btn.classList.remove("disabled");
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.afterLoad();
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
loadVL : function(that) {
|
||||
$.getJSON(versionsFileUrl, function(data) {
|
||||
all_versions = data;
|
||||
that.afterLoad();
|
||||
return true;
|
||||
}).fail(function() {
|
||||
console.log("Version Switch Error: versions.json could not be loaded.");
|
||||
that.$btn.addClass("disabled");
|
||||
return false;
|
||||
});
|
||||
},
|
||||
afterLoad : function() {
|
||||
var release = DOCUMENTATION_OPTIONS.VERSION;
|
||||
}).then(() => {
|
||||
let release = DOCUMENTATION_OPTIONS.VERSION;
|
||||
const m = release.match(/\d\.\d+/g);
|
||||
if (m) {
|
||||
release = m[0];
|
||||
|
@ -65,259 +62,274 @@ var Popover = function() {
|
|||
|
||||
this.warnOld(release, all_versions);
|
||||
|
||||
var version = this.getNamed(release);
|
||||
var list = this.buildList(version);
|
||||
const version = this.getNamed(release);
|
||||
this.buildList(version);
|
||||
|
||||
this.$list.children(":first-child").remove();
|
||||
this.$list.append(list);
|
||||
var that = this;
|
||||
this.$list.on("keydown", function(e) {
|
||||
this.list.firstElementChild.remove();
|
||||
const that = this;
|
||||
this.list.addEventListener("keydown", function(e) {
|
||||
that.keyMove(e);
|
||||
});
|
||||
|
||||
this.$btn.removeClass("wait");
|
||||
this.btn.classList.remove("wait");
|
||||
this.btnOpenHandler();
|
||||
this.$btn.on("mousedown", function(e) {
|
||||
this.btn.addEventListener("mousedown", function(e) {
|
||||
that.btnOpenHandler();
|
||||
e.preventDefault()
|
||||
});
|
||||
this.$btn.on("keydown", function(e) {
|
||||
this.btn.addEventListener("keydown", function(e) {
|
||||
if (that.btnKeyFilter(e)) {
|
||||
that.btnOpenHandler();
|
||||
}
|
||||
});
|
||||
},
|
||||
warnOld : function(release, all_versions) {
|
||||
// Note this is effectively disabled now, two issues must fixed:
|
||||
// * versions.js does not contain a current entry, because that leads to
|
||||
// duplicate version numbers in the menu. These need to be deduplicated.
|
||||
// * It only shows the warning after opening the menu to switch version
|
||||
// when versions.js is loaded. This is too late to be useful.
|
||||
var current = all_versions.current
|
||||
if (!current)
|
||||
{
|
||||
// console.log("Version Switch Error: no 'current' in version.json.");
|
||||
return;
|
||||
}
|
||||
const m = current.match(/\d\.\d+/g);
|
||||
if (m) {
|
||||
current = parseFloat(m[0]);
|
||||
}
|
||||
if (release < current) {
|
||||
var currentURL = window.location.pathname.replace(release, current);
|
||||
var warning = $('<div class="admonition warning"> ' +
|
||||
'<p class="first admonition-title">Note</p> ' +
|
||||
'<p class="last"> ' +
|
||||
'You are not using the most up to date version of the documentation. ' +
|
||||
'<a href="#"></a> is the newest version.' +
|
||||
'</p>' +
|
||||
'</div>');
|
||||
|
||||
warning.find('a').attr('href', currentURL).text(current);
|
||||
|
||||
var body = $("div.body");
|
||||
if (!body.length) {
|
||||
body = $("div.document");
|
||||
}
|
||||
body.prepend(warning);
|
||||
}
|
||||
},
|
||||
buildList : function(v) {
|
||||
var url = new URL(window.location.href);
|
||||
let pathSplit = [ "", "api", v ];
|
||||
if (url.pathname.startsWith("/api/")) {
|
||||
pathSplit.push(url.pathname.split('/').slice(3).join('/'));
|
||||
}
|
||||
else {
|
||||
pathSplit.push(url.pathname.substring(1));
|
||||
}
|
||||
if (this.type) {
|
||||
var dyn = all_versions;
|
||||
var cur = v;
|
||||
}
|
||||
var buf = [];
|
||||
var that = this;
|
||||
$.each(dyn, function(ix, title) {
|
||||
buf.push("<li");
|
||||
if (ix === cur) {
|
||||
buf.push(
|
||||
' class="selected" tabindex="-1" role="presentation"><span tabindex="-1" role="menuitem" aria-current="page">' +
|
||||
title + '</spanp></li>');
|
||||
}
|
||||
else {
|
||||
pathSplit[2 + that.type] = ix;
|
||||
var href = new URL(url);
|
||||
href.pathname = pathSplit.join('/');
|
||||
buf.push(' tabindex="-1" role="presentation"><a href ="' + href + '" tabindex="-1">' +
|
||||
title + '</a></li>');
|
||||
}
|
||||
});
|
||||
return buf.join('');
|
||||
},
|
||||
getNamed : function(v) {
|
||||
$.each(all_versions, function(ix, title) {
|
||||
if (ix === "master" || ix === "main" || ix === "latest") {
|
||||
var m = title.match(/\d\.\d[\w\d\.]*/)[0];
|
||||
if (parseFloat(m) == v) {
|
||||
v = ix;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return v;
|
||||
},
|
||||
dialogToggle : function(speed) {
|
||||
var wasClose = !this.isOpen;
|
||||
var that = this;
|
||||
if (!this.isOpen) {
|
||||
this.$btn.addClass("version-btn-open");
|
||||
this.$btn.attr("aria-pressed", true);
|
||||
this.$dialog.attr("aria-hidden", false);
|
||||
this.$dialog.fadeIn(speed, function() {
|
||||
that.$btn.parent().on("focusout", function(e) {
|
||||
that.focusoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
})
|
||||
that.$btn.parent().on("mouseleave", function(e) {
|
||||
that.mouseoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
});
|
||||
this.isOpen = true;
|
||||
}
|
||||
else {
|
||||
this.$btn.removeClass("version-btn-open");
|
||||
this.$btn.attr("aria-pressed", false);
|
||||
this.$dialog.attr("aria-hidden", true);
|
||||
this.$btn.parent().off("focusout");
|
||||
this.$btn.parent().off("mouseleave");
|
||||
this.$dialog.fadeOut(speed, function() {
|
||||
if (this.$sel) {
|
||||
this.$sel.attr("tabindex", -1);
|
||||
}
|
||||
that.$btn.attr("tabindex", 0);
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body) {
|
||||
that.$btn.focus();
|
||||
}
|
||||
});
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
if (wasClose) {
|
||||
if (this.$sel) {
|
||||
this.$sel.attr("tabindex", -1);
|
||||
}
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body) {
|
||||
var $nw = this.listEnter();
|
||||
$nw.attr("tabindex", 0);
|
||||
$nw.focus();
|
||||
this.$sel = $nw;
|
||||
}
|
||||
}
|
||||
},
|
||||
btnOpenHandler : function() {
|
||||
this.dialogToggle(300);
|
||||
},
|
||||
focusoutHandler : function() {
|
||||
var list = this.$list;
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
if (list.find(":focus").length === 0) {
|
||||
that.dialogToggle(200);
|
||||
}
|
||||
}, 200);
|
||||
},
|
||||
mouseoutHandler : function() {
|
||||
this.dialogToggle(200);
|
||||
},
|
||||
btnKeyFilter : function(e) {
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return false;
|
||||
}
|
||||
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
|
||||
e.key === "ArrowDown" || e.key === "ArrowUp") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
keyMove : function(e) {
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return true;
|
||||
}
|
||||
var p = true;
|
||||
var $nw = $(e.target);
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
$nw = this.listPrev($nw);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
$nw = this.listNext($nw);
|
||||
break;
|
||||
case "Home":
|
||||
$nw = this.listFirst();
|
||||
break;
|
||||
case "End":
|
||||
$nw = this.listLast();
|
||||
break;
|
||||
case "Escape":
|
||||
$nw = this.listExit();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
$nw = this.listExit();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
$nw = this.listExit();
|
||||
break;
|
||||
default:
|
||||
p = false;
|
||||
}
|
||||
if (p) {
|
||||
$nw.attr("tabindex", 0);
|
||||
$nw.focus();
|
||||
if (this.$sel) {
|
||||
this.$sel.attr("tabindex", -1);
|
||||
}
|
||||
this.$sel = $nw;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
listPrev : function($nw) {
|
||||
if ($nw.parent().prev().length !== 0) {
|
||||
return $nw.parent().prev().children(":first-child");
|
||||
}
|
||||
else {
|
||||
return this.listLast();
|
||||
}
|
||||
},
|
||||
listNext : function($nw) {
|
||||
if ($nw.parent().next().length !== 0) {
|
||||
return $nw.parent().next().children(":first-child");
|
||||
}
|
||||
else {
|
||||
return this.listFirst();
|
||||
}
|
||||
},
|
||||
listFirst : function() {
|
||||
return this.$list.children(":first-child").children(":first-child");
|
||||
},
|
||||
listLast : function() {
|
||||
return this.$list.children(":last-child").children(":first-child");
|
||||
},
|
||||
listExit : function() {
|
||||
this.mouseoutHandler();
|
||||
return this.$btn;
|
||||
},
|
||||
listEnter : function() {
|
||||
return this.$list.children(":first-child").children(":first-child");
|
||||
});
|
||||
}
|
||||
warnOld(release, all_versions)
|
||||
{
|
||||
// Note this is effectively disabled now, two issues must fixed:
|
||||
// * versions.js does not contain a current entry, because that leads to
|
||||
// duplicate version numbers in the menu. These need to be deduplicated.
|
||||
// * It only shows the warning after opening the menu to switch version
|
||||
// when versions.js is loaded. This is too late to be useful.
|
||||
let current = all_versions.current
|
||||
if (!current) {
|
||||
// console.log("Version Switch Error: no 'current' in version.json.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
return Popover
|
||||
}();
|
||||
const m = current.match(/\d\.\d+/g);
|
||||
if (m) {
|
||||
current = parseFloat(m[0]);
|
||||
}
|
||||
if (release < current) {
|
||||
const currentURL = window.location.pathname.replace(release, current);
|
||||
const warning =
|
||||
document.querySelector("template#version-warning").firstElementChild.cloneNode(true);
|
||||
const link = warning.querySelector('a');
|
||||
link.setAttribute('href', currentURL);
|
||||
link.textContent = current;
|
||||
|
||||
$(document).ready(function() {
|
||||
var lng_popover = new Popover("version-popover");
|
||||
});
|
||||
let body = document.querySelector("div.body");
|
||||
if (!body.length) {
|
||||
body = document.querySelector("div.document");
|
||||
}
|
||||
body.prepend(warning);
|
||||
}
|
||||
}
|
||||
buildList(v)
|
||||
{
|
||||
const url = new URL(window.location.href);
|
||||
let pathSplit = [ "", "api", v ];
|
||||
if (url.pathname.startsWith("/api/")) {
|
||||
pathSplit.push(url.pathname.split('/').slice(4).join('/'));
|
||||
}
|
||||
else {
|
||||
pathSplit.push(url.pathname.substring(1));
|
||||
}
|
||||
let dyn, cur;
|
||||
if (this.type) {
|
||||
dyn = all_versions;
|
||||
cur = v;
|
||||
}
|
||||
const that = this;
|
||||
const template = document.querySelector("template#version-entry").content;
|
||||
for (let [ix, title] of Object.entries(dyn)) {
|
||||
let clone;
|
||||
if (ix === cur) {
|
||||
clone = template.querySelector("li.selected").cloneNode(true);
|
||||
clone.querySelector("span").innerHTML = title;
|
||||
}
|
||||
else {
|
||||
pathSplit[1 + that.type] = ix;
|
||||
let href = new URL(url);
|
||||
href.pathname = pathSplit.join('/');
|
||||
clone = template.firstElementChild.cloneNode(true);
|
||||
const link = clone.querySelector("a");
|
||||
link.href = href;
|
||||
link.innerHTML = title;
|
||||
}
|
||||
that.list.append(clone);
|
||||
};
|
||||
return this.list;
|
||||
}
|
||||
getNamed(v)
|
||||
{
|
||||
for (let [ix, title] of Object.entries(all_versions)) {
|
||||
if (ix === "master" || ix === "main" || ix === "latest") {
|
||||
const m = title.match(/\d\.\d[\w\d\.]*/)[0];
|
||||
if (parseFloat(m) == v) {
|
||||
v = ix;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
return v;
|
||||
}
|
||||
dialogToggle(speed)
|
||||
{
|
||||
const wasClose = !this.isOpen;
|
||||
const that = this;
|
||||
if (!this.isOpen) {
|
||||
this.btn.classList.add("version-btn-open");
|
||||
this.btn.setAttribute("aria-pressed", true);
|
||||
this.dialog.setAttribute("aria-hidden", false);
|
||||
this.dialog.style.display = "block";
|
||||
this.dialog.animate({opacity : [ 0, 1 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
|
||||
.finished.then(() => {
|
||||
this.focusoutHandlerPrime = function(e) {
|
||||
that.focusoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
this.mouseoutHandlerPrime = function(e) {
|
||||
that.mouseoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
this.btn.parentNode.addEventListener("focusout", this.focusoutHandlerPrime);
|
||||
this.btn.parentNode.addEventListener("mouseleave", this.mouseoutHandlerPrime);
|
||||
});
|
||||
this.isOpen = true;
|
||||
}
|
||||
else {
|
||||
this.btn.classList.remove("version-btn-open");
|
||||
this.btn.setAttribute("aria-pressed", false);
|
||||
this.dialog.setAttribute("aria-hidden", true);
|
||||
this.btn.parentNode.removeEventListener("focusout", this.focusoutHandlerPrime);
|
||||
this.btn.parentNode.removeEventListener("mouseleave", this.mouseoutHandlerPrime);
|
||||
this.dialog.animate({opacity : [ 1, 0 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
|
||||
.finished.then(() => {
|
||||
this.dialog.style.display = "none";
|
||||
if (this.sel) {
|
||||
this.sel.setAttribute("tabindex", -1);
|
||||
}
|
||||
this.btn.setAttribute("tabindex", 0);
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body)
|
||||
{
|
||||
this.btn.focus();
|
||||
}
|
||||
});
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
if (wasClose) {
|
||||
if (this.sel) {
|
||||
this.sel.setAttribute("tabindex", -1);
|
||||
}
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body)
|
||||
{
|
||||
const nw = this.listEnter();
|
||||
nw.setAttribute("tabindex", 0);
|
||||
nw.focus();
|
||||
this.sel = nw;
|
||||
}
|
||||
}
|
||||
}
|
||||
btnOpenHandler()
|
||||
{
|
||||
this.dialogToggle(300);
|
||||
}
|
||||
focusoutHandler()
|
||||
{
|
||||
const list = this.list;
|
||||
const that = this;
|
||||
setTimeout(function() {
|
||||
if (!list.querySelector(":focus")) {
|
||||
that.dialogToggle(200);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
mouseoutHandler()
|
||||
{
|
||||
this.dialogToggle(200);
|
||||
}
|
||||
btnKeyFilter(e)
|
||||
{
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return false;
|
||||
}
|
||||
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
|
||||
e.key === "ArrowDown" || e.key === "ArrowUp")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
keyMove(e)
|
||||
{
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return true;
|
||||
}
|
||||
let nw = e.target;
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
nw = this.listPrev(nw);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
nw = this.listNext(nw);
|
||||
break;
|
||||
case "Home":
|
||||
nw = this.listFirst();
|
||||
break;
|
||||
case "End":
|
||||
nw = this.listLast();
|
||||
break;
|
||||
case "Escape":
|
||||
nw = this.listExit();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
nw = this.listExit();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
nw = this.listExit();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
nw.setAttribute("tabindex", 0);
|
||||
nw.focus();
|
||||
if (this.sel) {
|
||||
this.sel.setAttribute("tabindex", -1);
|
||||
}
|
||||
this.sel = nw;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
listPrev(nw)
|
||||
{
|
||||
if (nw.parentNode.previousElementSibling.length !== 0) {
|
||||
return nw.parentNode.previousElementSibling.firstElementChild;
|
||||
}
|
||||
else {
|
||||
return this.listLast();
|
||||
}
|
||||
}
|
||||
listNext(nw)
|
||||
{
|
||||
if (nw.parentNode.nextElementSibling.length !== 0) {
|
||||
return nw.parentNode.nextElementSibling.firstElementChild;
|
||||
}
|
||||
else {
|
||||
return this.listFirst();
|
||||
}
|
||||
}
|
||||
listFirst()
|
||||
{
|
||||
return this.list.firstElementChild.firstElementChild;
|
||||
}
|
||||
listLast()
|
||||
{
|
||||
return this.list.lastElementChild.firstElementChild;
|
||||
}
|
||||
listExit()
|
||||
{
|
||||
this.mouseoutHandler();
|
||||
return this.btn;
|
||||
}
|
||||
listEnter()
|
||||
{
|
||||
return this.list.firstElementChild.firstElementChild;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => { new Popover("version-popover"); });
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{%- extends "!base.html" -%}
|
||||
|
||||
{%- block theme_scripts -%}
|
||||
{{ super() }}
|
||||
<script defer data-domain="docs.blender.org" src="https://analytics.blender.org/js/script.js"></script>
|
||||
{%- endblock -%}
|
|
@ -1,6 +1,3 @@
|
|||
{# For the "Report Issue" button on the bottom of pages. #}
|
||||
{%- extends "!footer.html" %}
|
||||
{%- block extrafooter %}
|
||||
{%- if not pagename in ("search", "404", "genindex") and hasdoc(pagename) %}
|
||||
<div class="footer-contribute">
|
||||
<ul>
|
||||
|
@ -13,10 +10,9 @@
|
|||
#}Permanent+Link%5D%28https%3A%2F%2Fdocs.blender.org%2F{#
|
||||
#}api%2F{{ version }}%2F{{ pagename }}{{ file_suffix }}%29%0D%0A%0D%0A%2A%2A{#
|
||||
#}Short+description+of+error%2A%2A%0D%0A%5B{#
|
||||
#}Please+fill+out+a+short+description+of+the+error+here%5D%0D%0A"
|
||||
class="fa fa-bug"> {{ _('Report issue on this page') }}</a>
|
||||
#}Please+fill+out+a+short+description+of+the+error+here%5D%0D%0A" class="fa fa-bug"> {{ _('Report issue
|
||||
on this page') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{% endblock %}
|
||||
{%- endif %}
|
|
@ -0,0 +1,6 @@
|
|||
{%- extends "!page.html" -%}
|
||||
|
||||
{%- block footer -%}
|
||||
{{ super() }}
|
||||
{%- include "components/footer_contribute.html" -%}
|
||||
{%- endblock footer -%}
|
|
@ -0,0 +1,29 @@
|
|||
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
|
||||
<ul id="versionwrap" role="presentation">
|
||||
<li role="presentation">
|
||||
<span id="version-popover" class="version-btn" tabindex="0" role="button" aria-label="versions selector"
|
||||
aria-haspopup="true" aria-controls="version-vsnlist" aria-disabled="true">
|
||||
{{ release }}
|
||||
</span>
|
||||
<div class="version-dialog" aria-hidden="true">
|
||||
<div class="version-title">Versions</div>
|
||||
<ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true">
|
||||
<li role="presentation">Loading...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<template id="version-entry">
|
||||
<li tabindex="-1" role="presentation"><a tabindex="-1" role="menuitem"></a></li>
|
||||
<li class="selected" tabindex="-1" role="presentation"><span tabindex="-1" aria-current="page"></span></li>
|
||||
</template>
|
||||
</ul>
|
||||
<template id="version-warning">
|
||||
<div class="admonition warning">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">
|
||||
You are not using the most up to date version of the documentation.
|
||||
<a href="#"></a> is the newest version.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
|
@ -1,19 +0,0 @@
|
|||
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
|
||||
<ul id="versionwrap" role="presentation">
|
||||
<li role="presentation">
|
||||
<span id="version-popover" class="version-btn" tabindex="0" role="button"
|
||||
aria-label="versions selector" aria-haspopup="true" aria-controls="version-vsnlist"
|
||||
aria-disabled="true">
|
||||
{{ release }}
|
||||
</span>
|
||||
<div class="version-dialog" aria-hidden="true">
|
||||
<div class="version-arrow" aria-hidden="true"></div>
|
||||
<div class="version-title">Versions</div>
|
||||
<ul id="version-vsnlist" class="version-list" role="menu"
|
||||
aria-labelledby="version-popover" aria-hidden="true">
|
||||
<li role="presentation">Loading...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,4 +1,3 @@
|
|||
When updating a library remember to:
|
||||
|
||||
* Update the README.blender with the corresponding version.
|
||||
* Update the THIRD-PARTY-LICENSES.txt document
|
||||
|
|
|
@ -1594,14 +1594,15 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
compute_device_type = self.get_compute_device_type()
|
||||
|
||||
# We need non-CPU devices, used for rendering and supporting OIDN GPU denoising
|
||||
for device in _cycles.available_devices(compute_device_type):
|
||||
device_type = device[1]
|
||||
if device_type == 'CPU':
|
||||
continue
|
||||
if compute_device_type != 'NONE':
|
||||
for device in _cycles.available_devices(compute_device_type):
|
||||
device_type = device[1]
|
||||
if device_type == 'CPU':
|
||||
continue
|
||||
|
||||
has_device_oidn_support = device[5]
|
||||
if has_device_oidn_support and self.find_existing_device_entry(device).use:
|
||||
return True
|
||||
has_device_oidn_support = device[5]
|
||||
if has_device_oidn_support and self.find_existing_device_entry(device).use:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ BVHOptiX::~BVHOptiX()
|
|||
{
|
||||
/* Acceleration structure memory is delayed freed on device, since deleting the
|
||||
* BVH may happen while still being used for rendering. */
|
||||
device->release_optix_bvh(this);
|
||||
device->release_bvh(this);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -217,11 +217,10 @@ class Device {
|
|||
/* Get OpenShadingLanguage memory buffer. */
|
||||
virtual void *get_cpu_osl_memory();
|
||||
|
||||
/* acceleration structure building */
|
||||
/* Acceleration structure building. */
|
||||
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
|
||||
|
||||
/* OptiX specific destructor. */
|
||||
virtual void release_optix_bvh(BVH * /*bvh*/){};
|
||||
/* Used by Metal and OptiX. */
|
||||
virtual void release_bvh(BVH * /*bvh*/) {}
|
||||
|
||||
/* multi device */
|
||||
virtual int device_number(Device * /*sub_device*/)
|
||||
|
|
|
@ -28,9 +28,9 @@ class BVHMetal : public BVH {
|
|||
API_AVAILABLE(macos(11.0))
|
||||
vector<id<MTLAccelerationStructure>> unique_blas_array;
|
||||
|
||||
bool motion_blur = false;
|
||||
Device *device = nullptr;
|
||||
|
||||
Stats &stats;
|
||||
bool motion_blur = false;
|
||||
|
||||
bool build(Progress &progress, id<MTLDevice> device, id<MTLCommandQueue> queue, bool refit);
|
||||
|
||||
|
|
|
@ -113,15 +113,18 @@ BVHMetal::BVHMetal(const BVHParams ¶ms_,
|
|||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_,
|
||||
Device *device)
|
||||
: BVH(params_, geometry_, objects_), stats(device->stats)
|
||||
: BVH(params_, geometry_, objects_), device(device)
|
||||
{
|
||||
}
|
||||
|
||||
BVHMetal::~BVHMetal()
|
||||
{
|
||||
/* Clear point used by enqueueing. */
|
||||
device->release_bvh(this);
|
||||
|
||||
if (@available(macos 12.0, *)) {
|
||||
if (accel_struct) {
|
||||
stats.mem_free(accel_struct.allocatedSize);
|
||||
device->stats.mem_free(accel_struct.allocatedSize);
|
||||
[accel_struct release];
|
||||
}
|
||||
|
||||
|
@ -132,7 +135,7 @@ BVHMetal::~BVHMetal()
|
|||
}
|
||||
|
||||
bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
||||
id<MTLDevice> device,
|
||||
id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue,
|
||||
Geometry *const geom,
|
||||
bool refit)
|
||||
|
@ -163,7 +166,7 @@ bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
|||
}
|
||||
|
||||
MTLResourceOptions storage_mode;
|
||||
if (device.hasUnifiedMemory) {
|
||||
if (mtl_device.hasUnifiedMemory) {
|
||||
storage_mode = MTLResourceStorageModeShared;
|
||||
}
|
||||
else {
|
||||
|
@ -172,18 +175,19 @@ bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
|||
|
||||
/* Upload the mesh data to the GPU */
|
||||
id<MTLBuffer> posBuf = nil;
|
||||
id<MTLBuffer> indexBuf = [device newBufferWithBytes:tris.data()
|
||||
length:num_indices * sizeof(tris.data()[0])
|
||||
options:storage_mode];
|
||||
id<MTLBuffer> indexBuf = [mtl_device newBufferWithBytes:tris.data()
|
||||
length:num_indices * sizeof(tris.data()[0])
|
||||
options:storage_mode];
|
||||
|
||||
if (num_motion_steps == 1) {
|
||||
posBuf = [device newBufferWithBytes:verts.data()
|
||||
length:num_verts * sizeof(verts.data()[0])
|
||||
options:storage_mode];
|
||||
posBuf = [mtl_device newBufferWithBytes:verts.data()
|
||||
length:num_verts * sizeof(verts.data()[0])
|
||||
options:storage_mode];
|
||||
}
|
||||
else {
|
||||
posBuf = [device newBufferWithLength:num_verts * num_motion_steps * sizeof(verts.data()[0])
|
||||
options:storage_mode];
|
||||
posBuf = [mtl_device
|
||||
newBufferWithLength:num_verts * num_motion_steps * sizeof(verts.data()[0])
|
||||
options:storage_mode];
|
||||
float3 *dest_data = (float3 *)[posBuf contents];
|
||||
size_t center_step = (num_motion_steps - 1) / 2;
|
||||
for (size_t step = 0; step < num_motion_steps; ++step) {
|
||||
|
@ -264,13 +268,14 @@ bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
|||
MTLAccelerationStructureUsagePreferFastBuild);
|
||||
}
|
||||
|
||||
MTLAccelerationStructureSizes accelSizes = [device
|
||||
MTLAccelerationStructureSizes accelSizes = [mtl_device
|
||||
accelerationStructureSizesWithDescriptor:accelDesc];
|
||||
id<MTLAccelerationStructure> accel_uncompressed = [device
|
||||
id<MTLAccelerationStructure> accel_uncompressed = [mtl_device
|
||||
newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
|
||||
id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [device newBufferWithLength:8 options:MTLResourceStorageModeShared];
|
||||
id<MTLBuffer> scratchBuf = [mtl_device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [mtl_device newBufferWithLength:8
|
||||
options:MTLResourceStorageModeShared];
|
||||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
|
@ -314,14 +319,14 @@ bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
|||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
id<MTLAccelerationStructure> accel = [device
|
||||
id<MTLAccelerationStructure> accel = [mtl_device
|
||||
newAccelerationStructureWithSize:compressed_size];
|
||||
[accelEnc copyAndCompactAccelerationStructure:accel_uncompressed
|
||||
toAccelerationStructure:accel];
|
||||
[accelEnc endEncoding];
|
||||
[accelCommands addCompletedHandler:^(id<MTLCommandBuffer> /*command_buffer*/) {
|
||||
uint64_t allocated_size = [accel allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
accel_struct = accel;
|
||||
[accel_uncompressed release];
|
||||
|
||||
|
@ -336,7 +341,7 @@ bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
|||
accel_struct = accel_uncompressed;
|
||||
|
||||
uint64_t allocated_size = [accel_struct allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
|
||||
/* Signal that we've finished doing GPU acceleration struct build. */
|
||||
g_bvh_build_throttler.release(wired_size);
|
||||
|
@ -355,7 +360,7 @@ bool BVHMetal::build_BLAS_mesh(Progress &progress,
|
|||
}
|
||||
|
||||
bool BVHMetal::build_BLAS_hair(Progress &progress,
|
||||
id<MTLDevice> device,
|
||||
id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue,
|
||||
Geometry *const geom,
|
||||
bool refit)
|
||||
|
@ -382,7 +387,7 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
}
|
||||
|
||||
MTLResourceOptions storage_mode;
|
||||
if (device.hasUnifiedMemory) {
|
||||
if (mtl_device.hasUnifiedMemory) {
|
||||
storage_mode = MTLResourceStorageModeShared;
|
||||
}
|
||||
else {
|
||||
|
@ -445,18 +450,18 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
}
|
||||
|
||||
/* Allocate and populate MTLBuffers for geometry. */
|
||||
idxBuffer = [device newBufferWithBytes:idxData.data()
|
||||
length:idxData.size() * sizeof(int)
|
||||
options:storage_mode];
|
||||
idxBuffer = [mtl_device newBufferWithBytes:idxData.data()
|
||||
length:idxData.size() * sizeof(int)
|
||||
options:storage_mode];
|
||||
|
||||
cpBuffer = [device newBufferWithBytes:cpData.data()
|
||||
length:cpData.size() * sizeof(float3)
|
||||
options:storage_mode];
|
||||
|
||||
radiusBuffer = [device newBufferWithBytes:radiusData.data()
|
||||
length:radiusData.size() * sizeof(float)
|
||||
cpBuffer = [mtl_device newBufferWithBytes:cpData.data()
|
||||
length:cpData.size() * sizeof(float3)
|
||||
options:storage_mode];
|
||||
|
||||
radiusBuffer = [mtl_device newBufferWithBytes:radiusData.data()
|
||||
length:radiusData.size() * sizeof(float)
|
||||
options:storage_mode];
|
||||
|
||||
std::vector<MTLMotionKeyframeData *> cp_ptrs;
|
||||
std::vector<MTLMotionKeyframeData *> radius_ptrs;
|
||||
cp_ptrs.reserve(num_motion_steps);
|
||||
|
@ -541,18 +546,18 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
}
|
||||
|
||||
/* Allocate and populate MTLBuffers for geometry. */
|
||||
idxBuffer = [device newBufferWithBytes:idxData.data()
|
||||
length:idxData.size() * sizeof(int)
|
||||
options:storage_mode];
|
||||
idxBuffer = [mtl_device newBufferWithBytes:idxData.data()
|
||||
length:idxData.size() * sizeof(int)
|
||||
options:storage_mode];
|
||||
|
||||
cpBuffer = [device newBufferWithBytes:cpData.data()
|
||||
length:cpData.size() * sizeof(float3)
|
||||
options:storage_mode];
|
||||
|
||||
radiusBuffer = [device newBufferWithBytes:radiusData.data()
|
||||
length:radiusData.size() * sizeof(float)
|
||||
cpBuffer = [mtl_device newBufferWithBytes:cpData.data()
|
||||
length:cpData.size() * sizeof(float3)
|
||||
options:storage_mode];
|
||||
|
||||
radiusBuffer = [mtl_device newBufferWithBytes:radiusData.data()
|
||||
length:radiusData.size() * sizeof(float)
|
||||
options:storage_mode];
|
||||
|
||||
if (storage_mode == MTLResourceStorageModeManaged) {
|
||||
[cpBuffer didModifyRange:NSMakeRange(0, cpBuffer.length)];
|
||||
[idxBuffer didModifyRange:NSMakeRange(0, idxBuffer.length)];
|
||||
|
@ -600,13 +605,14 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
}
|
||||
accelDesc.usage |= MTLAccelerationStructureUsageExtendedLimits;
|
||||
|
||||
MTLAccelerationStructureSizes accelSizes = [device
|
||||
MTLAccelerationStructureSizes accelSizes = [mtl_device
|
||||
accelerationStructureSizesWithDescriptor:accelDesc];
|
||||
id<MTLAccelerationStructure> accel_uncompressed = [device
|
||||
id<MTLAccelerationStructure> accel_uncompressed = [mtl_device
|
||||
newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
|
||||
id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [device newBufferWithLength:8 options:MTLResourceStorageModeShared];
|
||||
id<MTLBuffer> scratchBuf = [mtl_device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [mtl_device newBufferWithLength:8
|
||||
options:MTLResourceStorageModeShared];
|
||||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
|
@ -651,14 +657,14 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
id<MTLAccelerationStructure> accel = [device
|
||||
id<MTLAccelerationStructure> accel = [mtl_device
|
||||
newAccelerationStructureWithSize:compressed_size];
|
||||
[accelEnc copyAndCompactAccelerationStructure:accel_uncompressed
|
||||
toAccelerationStructure:accel];
|
||||
[accelEnc endEncoding];
|
||||
[accelCommands addCompletedHandler:^(id<MTLCommandBuffer> /*command_buffer*/) {
|
||||
uint64_t allocated_size = [accel allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
accel_struct = accel;
|
||||
[accel_uncompressed release];
|
||||
|
||||
|
@ -673,7 +679,7 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
accel_struct = accel_uncompressed;
|
||||
|
||||
uint64_t allocated_size = [accel_struct allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
|
||||
/* Signal that we've finished doing GPU acceleration struct build. */
|
||||
g_bvh_build_throttler.release(wired_size);
|
||||
|
@ -690,7 +696,7 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
}
|
||||
# else /* MAC_OS_VERSION_14_0 */
|
||||
(void)progress;
|
||||
(void)device;
|
||||
(void)mtl_device;
|
||||
(void)queue;
|
||||
(void)geom;
|
||||
(void)(refit);
|
||||
|
@ -699,7 +705,7 @@ bool BVHMetal::build_BLAS_hair(Progress &progress,
|
|||
}
|
||||
|
||||
bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
||||
id<MTLDevice> device,
|
||||
id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue,
|
||||
Geometry *const geom,
|
||||
bool refit)
|
||||
|
@ -732,7 +738,7 @@ bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
|||
const size_t num_aabbs = num_motion_steps * num_points;
|
||||
|
||||
MTLResourceOptions storage_mode;
|
||||
if (device.hasUnifiedMemory) {
|
||||
if (mtl_device.hasUnifiedMemory) {
|
||||
storage_mode = MTLResourceStorageModeShared;
|
||||
}
|
||||
else {
|
||||
|
@ -740,7 +746,7 @@ bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
|||
}
|
||||
|
||||
/* Allocate a GPU buffer for the AABB data and populate it */
|
||||
id<MTLBuffer> aabbBuf = [device
|
||||
id<MTLBuffer> aabbBuf = [mtl_device
|
||||
newBufferWithLength:num_aabbs * sizeof(MTLAxisAlignedBoundingBox)
|
||||
options:storage_mode];
|
||||
MTLAxisAlignedBoundingBox *aabb_data = (MTLAxisAlignedBoundingBox *)[aabbBuf contents];
|
||||
|
@ -848,13 +854,14 @@ bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
|||
MTLAccelerationStructureUsagePreferFastBuild);
|
||||
}
|
||||
|
||||
MTLAccelerationStructureSizes accelSizes = [device
|
||||
MTLAccelerationStructureSizes accelSizes = [mtl_device
|
||||
accelerationStructureSizesWithDescriptor:accelDesc];
|
||||
id<MTLAccelerationStructure> accel_uncompressed = [device
|
||||
id<MTLAccelerationStructure> accel_uncompressed = [mtl_device
|
||||
newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
|
||||
id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [device newBufferWithLength:8 options:MTLResourceStorageModeShared];
|
||||
id<MTLBuffer> scratchBuf = [mtl_device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [mtl_device newBufferWithLength:8
|
||||
options:MTLResourceStorageModeShared];
|
||||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
|
@ -897,14 +904,14 @@ bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
|||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
id<MTLAccelerationStructure> accel = [device
|
||||
id<MTLAccelerationStructure> accel = [mtl_device
|
||||
newAccelerationStructureWithSize:compressed_size];
|
||||
[accelEnc copyAndCompactAccelerationStructure:accel_uncompressed
|
||||
toAccelerationStructure:accel];
|
||||
[accelEnc endEncoding];
|
||||
[accelCommands addCompletedHandler:^(id<MTLCommandBuffer> /*command_buffer*/) {
|
||||
uint64_t allocated_size = [accel allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
accel_struct = accel;
|
||||
[accel_uncompressed release];
|
||||
|
||||
|
@ -919,7 +926,7 @@ bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
|||
accel_struct = accel_uncompressed;
|
||||
|
||||
uint64_t allocated_size = [accel_struct allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
|
||||
/* Signal that we've finished doing GPU acceleration struct build. */
|
||||
g_bvh_build_throttler.release(wired_size);
|
||||
|
@ -937,7 +944,7 @@ bool BVHMetal::build_BLAS_pointcloud(Progress &progress,
|
|||
}
|
||||
|
||||
bool BVHMetal::build_BLAS(Progress &progress,
|
||||
id<MTLDevice> device,
|
||||
id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue,
|
||||
bool refit)
|
||||
{
|
||||
|
@ -948,11 +955,11 @@ bool BVHMetal::build_BLAS(Progress &progress,
|
|||
switch (geom->geometry_type) {
|
||||
case Geometry::VOLUME:
|
||||
case Geometry::MESH:
|
||||
return build_BLAS_mesh(progress, device, queue, geom, refit);
|
||||
return build_BLAS_mesh(progress, mtl_device, queue, geom, refit);
|
||||
case Geometry::HAIR:
|
||||
return build_BLAS_hair(progress, device, queue, geom, refit);
|
||||
return build_BLAS_hair(progress, mtl_device, queue, geom, refit);
|
||||
case Geometry::POINTCLOUD:
|
||||
return build_BLAS_pointcloud(progress, device, queue, geom, refit);
|
||||
return build_BLAS_pointcloud(progress, mtl_device, queue, geom, refit);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -960,7 +967,7 @@ bool BVHMetal::build_BLAS(Progress &progress,
|
|||
}
|
||||
|
||||
bool BVHMetal::build_TLAS(Progress &progress,
|
||||
id<MTLDevice> device,
|
||||
id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue,
|
||||
bool refit)
|
||||
{
|
||||
|
@ -969,14 +976,14 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
|
||||
if (@available(macos 12.0, *)) {
|
||||
/* Defined inside available check, for return type to be available. */
|
||||
auto make_null_BLAS = [](id<MTLDevice> device,
|
||||
auto make_null_BLAS = [](id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue) -> id<MTLAccelerationStructure> {
|
||||
MTLResourceOptions storage_mode = MTLResourceStorageModeManaged;
|
||||
if (device.hasUnifiedMemory) {
|
||||
if (mtl_device.hasUnifiedMemory) {
|
||||
storage_mode = MTLResourceStorageModeShared;
|
||||
}
|
||||
|
||||
id<MTLBuffer> nullBuf = [device newBufferWithLength:sizeof(float3) options:storage_mode];
|
||||
id<MTLBuffer> nullBuf = [mtl_device newBufferWithLength:sizeof(float3) options:storage_mode];
|
||||
|
||||
/* Create an acceleration structure. */
|
||||
MTLAccelerationStructureTriangleGeometryDescriptor *geomDesc =
|
||||
|
@ -997,13 +1004,14 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
accelDesc.geometryDescriptors = @[ geomDesc ];
|
||||
accelDesc.usage |= MTLAccelerationStructureUsageExtendedLimits;
|
||||
|
||||
MTLAccelerationStructureSizes accelSizes = [device
|
||||
MTLAccelerationStructureSizes accelSizes = [mtl_device
|
||||
accelerationStructureSizesWithDescriptor:accelDesc];
|
||||
id<MTLAccelerationStructure> accel_struct = [device
|
||||
id<MTLAccelerationStructure> accel_struct = [mtl_device
|
||||
newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
|
||||
id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [device newBufferWithLength:8 options:MTLResourceStorageModeShared];
|
||||
id<MTLBuffer> scratchBuf = [mtl_device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> sizeBuf = [mtl_device newBufferWithLength:8
|
||||
options:MTLResourceStorageModeShared];
|
||||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
|
@ -1070,7 +1078,7 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
};
|
||||
|
||||
MTLResourceOptions storage_mode;
|
||||
if (device.hasUnifiedMemory) {
|
||||
if (mtl_device.hasUnifiedMemory) {
|
||||
storage_mode = MTLResourceStorageModeShared;
|
||||
}
|
||||
else {
|
||||
|
@ -1086,12 +1094,12 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
}
|
||||
|
||||
/* Allocate a GPU buffer for the instance data and populate it */
|
||||
id<MTLBuffer> instanceBuf = [device newBufferWithLength:num_instances * instance_size
|
||||
options:storage_mode];
|
||||
id<MTLBuffer> instanceBuf = [mtl_device newBufferWithLength:num_instances * instance_size
|
||||
options:storage_mode];
|
||||
id<MTLBuffer> motion_transforms_buf = nil;
|
||||
MTLPackedFloat4x3 *motion_transforms = nullptr;
|
||||
if (motion_blur && num_motion_transforms) {
|
||||
motion_transforms_buf = [device
|
||||
motion_transforms_buf = [mtl_device
|
||||
newBufferWithLength:num_motion_transforms * sizeof(MTLPackedFloat4x3)
|
||||
options:storage_mode];
|
||||
motion_transforms = (MTLPackedFloat4x3 *)motion_transforms_buf.contents;
|
||||
|
@ -1108,14 +1116,14 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
Geometry const *geom = ob->get_geometry();
|
||||
BVHMetal const *blas = static_cast<BVHMetal const *>(geom->bvh);
|
||||
if (!blas || !blas->accel_struct) {
|
||||
/* Place a degenerate instance, to ensure [[instance_id]] equals ob->get_device_index()
|
||||
/* Place a degenerate instance, to ensure [[instance_id]] equals ob->get_mtl_device_index()
|
||||
* in our intersection functions */
|
||||
blas = nullptr;
|
||||
|
||||
/* Workaround for issue in macOS <= 14.1: Insert degenerate BLAS instead of zero-filling
|
||||
* the descriptor. */
|
||||
if (!null_BLAS) {
|
||||
null_BLAS = make_null_BLAS(device, queue);
|
||||
null_BLAS = make_null_BLAS(mtl_device, queue);
|
||||
}
|
||||
blas_array.push_back(null_BLAS);
|
||||
}
|
||||
|
@ -1259,12 +1267,12 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
MTLAccelerationStructureUsagePreferFastBuild);
|
||||
}
|
||||
|
||||
MTLAccelerationStructureSizes accelSizes = [device
|
||||
MTLAccelerationStructureSizes accelSizes = [mtl_device
|
||||
accelerationStructureSizesWithDescriptor:accelDesc];
|
||||
id<MTLAccelerationStructure> accel = [device
|
||||
id<MTLAccelerationStructure> accel = [mtl_device
|
||||
newAccelerationStructureWithSize:accelSizes.accelerationStructureSize];
|
||||
id<MTLBuffer> scratchBuf = [device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLBuffer> scratchBuf = [mtl_device newBufferWithLength:accelSizes.buildScratchBufferSize
|
||||
options:MTLResourceStorageModePrivate];
|
||||
id<MTLCommandBuffer> accelCommands = [queue commandBuffer];
|
||||
id<MTLAccelerationStructureCommandEncoder> accelEnc =
|
||||
[accelCommands accelerationStructureCommandEncoder];
|
||||
|
@ -1292,7 +1300,7 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
[scratchBuf release];
|
||||
|
||||
uint64_t allocated_size = [accel allocatedSize];
|
||||
stats.mem_alloc(allocated_size);
|
||||
device->stats.mem_alloc(allocated_size);
|
||||
|
||||
/* Cache top and bottom-level acceleration structs */
|
||||
accel_struct = accel;
|
||||
|
@ -1309,7 +1317,7 @@ bool BVHMetal::build_TLAS(Progress &progress,
|
|||
}
|
||||
|
||||
bool BVHMetal::build(Progress &progress,
|
||||
id<MTLDevice> device,
|
||||
id<MTLDevice> mtl_device,
|
||||
id<MTLCommandQueue> queue,
|
||||
bool refit)
|
||||
{
|
||||
|
@ -1319,7 +1327,7 @@ bool BVHMetal::build(Progress &progress,
|
|||
}
|
||||
else {
|
||||
if (accel_struct) {
|
||||
stats.mem_free(accel_struct.allocatedSize);
|
||||
device->stats.mem_free(accel_struct.allocatedSize);
|
||||
[accel_struct release];
|
||||
accel_struct = nil;
|
||||
}
|
||||
|
@ -1328,10 +1336,10 @@ bool BVHMetal::build(Progress &progress,
|
|||
|
||||
@autoreleasepool {
|
||||
if (!params.top_level) {
|
||||
return build_BLAS(progress, device, queue, refit);
|
||||
return build_BLAS(progress, mtl_device, queue, refit);
|
||||
}
|
||||
else {
|
||||
return build_TLAS(progress, device, queue, refit);
|
||||
return build_TLAS(progress, mtl_device, queue, refit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,8 @@ class MetalDevice : public Device {
|
|||
|
||||
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit) override;
|
||||
|
||||
virtual void release_bvh(BVH *bvh) override;
|
||||
|
||||
virtual void optimize_for_scene(Scene *scene) override;
|
||||
|
||||
static void compile_and_load(int device_id, MetalPipelineType pso_type);
|
||||
|
|
|
@ -341,7 +341,7 @@ string MetalDevice::preprocess_source(MetalPipelineType pso_type,
|
|||
}
|
||||
|
||||
# ifdef WITH_CYCLES_DEBUG
|
||||
global_defines += "#define __KERNEL_DEBUG__\n";
|
||||
global_defines += "#define WITH_CYCLES_DEBUG\n";
|
||||
# endif
|
||||
|
||||
switch (device_vendor) {
|
||||
|
@ -1443,6 +1443,13 @@ void MetalDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
|
|||
}
|
||||
}
|
||||
|
||||
void MetalDevice::release_bvh(BVH *bvh)
|
||||
{
|
||||
if (bvhMetalRT == bvh) {
|
||||
bvhMetalRT = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1680,7 +1680,7 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
|
|||
}
|
||||
}
|
||||
|
||||
void OptiXDevice::release_optix_bvh(BVH *bvh)
|
||||
void OptiXDevice::release_bvh(BVH *bvh)
|
||||
{
|
||||
thread_scoped_lock lock(delayed_free_bvh_mutex);
|
||||
/* Do delayed free of BVH memory, since geometry holding BVH might be deleted
|
||||
|
|
|
@ -106,7 +106,7 @@ class OptiXDevice : public CUDADevice {
|
|||
|
||||
void build_bvh(BVH *bvh, Progress &progress, bool refit) override;
|
||||
|
||||
void release_optix_bvh(BVH *bvh) override;
|
||||
void release_bvh(BVH *bvh) override;
|
||||
void free_bvh_memory_delayed();
|
||||
|
||||
void const_copy_to(const char *name, void *host, size_t size) override;
|
||||
|
|
|
@ -144,7 +144,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
#if defined(__KERNEL_DEBUG__)
|
||||
#if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
isect->t = ray->tmax;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
|
@ -281,7 +281,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(__KERNEL_DEBUG__)
|
||||
# if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
if (local_isect) {
|
||||
local_isect->num_hits = 0;
|
||||
|
@ -383,7 +383,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(__KERNEL_DEBUG__)
|
||||
# if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer");
|
||||
return false;
|
||||
|
@ -446,7 +446,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(__KERNEL_DEBUG__)
|
||||
# if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer");
|
||||
return false;
|
||||
|
|
|
@ -280,6 +280,16 @@ ccl_device
|
|||
|
||||
kernel_assert(ls.pdf != 0.0f);
|
||||
|
||||
const bool is_transmission = dot(ls.D, sd->N) < 0.0f;
|
||||
|
||||
if (ls.prim != PRIM_NONE && ls.prim == sd->prim && ls.object == sd->object) {
|
||||
/* Skip self intersection if light direction lies in the same hemisphere as the geometric
|
||||
* normal. */
|
||||
if (dot(ls.D, is_transmission ? -sd->Ng : sd->Ng) > 0.0f) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Evaluate light shader.
|
||||
*
|
||||
* TODO: can we reuse sd memory? In theory we can move this after
|
||||
|
@ -292,8 +302,6 @@ ccl_device
|
|||
Ray ray ccl_optional_struct_init;
|
||||
BsdfEval bsdf_eval ccl_optional_struct_init;
|
||||
|
||||
const bool is_transmission = dot(ls.D, sd->N) < 0.0f;
|
||||
|
||||
int mnee_vertex_count = 0;
|
||||
#ifdef __MNEE__
|
||||
IF_KERNEL_FEATURE(MNEE)
|
||||
|
|
|
@ -313,32 +313,6 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
|
|||
return pdf;
|
||||
}
|
||||
|
||||
ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray,
|
||||
const float3 light_P,
|
||||
const float sample_t)
|
||||
{
|
||||
float delta = dot((light_P - ray->P), ray->D);
|
||||
float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
|
||||
if (UNLIKELY(D == 0.0f)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float tmin = ray->tmin;
|
||||
const float tmax = ray->tmax;
|
||||
const float t_ = sample_t - delta;
|
||||
|
||||
const float theta_a = atan2f(tmin - delta, D);
|
||||
const float theta_b = atan2f(tmax - delta, D);
|
||||
if (UNLIKELY(theta_b == theta_a)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float theta_sample = atan2f(t_, D);
|
||||
const float cdf = (theta_sample - theta_a) / (theta_b - theta_a);
|
||||
|
||||
return cdf;
|
||||
}
|
||||
|
||||
/* Distance sampling */
|
||||
|
||||
ccl_device float volume_distance_sample(float max_t,
|
||||
|
|
|
@ -212,7 +212,7 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
|
|||
/* BSDF */
|
||||
#ifdef WITH_CYCLES_DEBUG
|
||||
ccl_device_inline void surface_shader_validate_bsdf_sample(const KernelGlobals kg,
|
||||
const ShaderClosure *sc,
|
||||
ccl_private const ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
const int org_label,
|
||||
const float2 org_roughness,
|
||||
|
|
|
@ -311,7 +311,7 @@ ccl_device_inline bool area_light_eval(const ccl_global KernelLight *klight,
|
|||
ls->pdf *= light_pdf_area_to_solid_angle(Ng, -ls->D, ls->t);
|
||||
}
|
||||
|
||||
return ls->eval_fac > 0;
|
||||
return in_volume_segment || ls->eval_fac > 0;
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Transform vector to spot light's local coordinate system. */
|
||||
ccl_device float3 spot_light_to_local(const ccl_global KernelSpotLight *spot, const float3 ray)
|
||||
ccl_device float3 spot_light_to_local(const ccl_global KernelLight *klight, const float3 ray)
|
||||
{
|
||||
return safe_normalize(make_float3(dot(ray, spot->scaled_axis_u),
|
||||
dot(ray, spot->scaled_axis_v),
|
||||
dot(ray, spot->dir * spot->inv_len_z)));
|
||||
const Transform itfm = klight->itfm;
|
||||
float3 transformed_ray = safe_normalize(transform_direction(&itfm, ray));
|
||||
transformed_ray.z = -transformed_ray.z;
|
||||
|
||||
return transformed_ray;
|
||||
}
|
||||
|
||||
/* Compute spot light attenuation of a ray given in local coordinate system. */
|
||||
|
@ -58,7 +60,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
ls->t = FLT_MAX;
|
||||
if (d_sq > r_sq) {
|
||||
/* Outside sphere. */
|
||||
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_spot_angle;
|
||||
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_larger_spread;
|
||||
const float one_minus_cos_half_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq);
|
||||
|
||||
if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) {
|
||||
|
@ -92,7 +94,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
}
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ls->D);
|
||||
if (d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
@ -128,7 +130,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
ls->Ng = -ls->D;
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ls->D);
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
if (!in_volume_segment && ls->eval_fac == 0.0f) {
|
||||
return false;
|
||||
|
@ -145,7 +147,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
return true;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
|
||||
ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *spot,
|
||||
const float d_sq,
|
||||
const float r_sq,
|
||||
const float3 N,
|
||||
|
@ -153,7 +155,8 @@ ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
|
|||
const uint32_t path_flag)
|
||||
{
|
||||
if (d_sq > r_sq) {
|
||||
return M_1_2PI_F / min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - cos_half_spread);
|
||||
return M_1_2PI_F /
|
||||
min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - spot->cos_half_larger_spread);
|
||||
}
|
||||
|
||||
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
|
||||
|
@ -181,7 +184,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
|
|||
/* NOTE : preserve pdf in area measure. */
|
||||
const float jacobian_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) /
|
||||
(radius * ls->t * t_sq);
|
||||
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ls->D, path_flag) *
|
||||
ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ls->D, path_flag) *
|
||||
jacobian_solid_angle_to_area;
|
||||
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
|
@ -196,7 +199,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
|
|||
}
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ls->D);
|
||||
if (use_attenuation) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
@ -232,7 +235,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
|||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
if (klight->spot.is_sphere) {
|
||||
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ray_D, path_flag);
|
||||
ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ray_D, path_flag);
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
}
|
||||
else {
|
||||
|
@ -248,7 +251,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
|||
}
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ray_D);
|
||||
if (!klight->spot.is_sphere || d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
|
|
@ -146,7 +146,9 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
|
|||
ls->shader = kernel_data_fetch(tri_shader, prim);
|
||||
const float distance_to_plane = dot(N0, V[0] - P) / dot(N0, N0);
|
||||
const int ls_shader_flag = kernel_data_fetch(shaders, ls->shader & SHADER_MASK).flags;
|
||||
if (!(ls_shader_flag & (distance_to_plane > 0 ? SD_MIS_BACK : SD_MIS_FRONT))) {
|
||||
if (!in_volume_segment &&
|
||||
!(ls_shader_flag & (distance_to_plane > 0 ? SD_MIS_BACK : SD_MIS_FRONT)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1367,16 +1367,15 @@ typedef struct KernelCurveSegment {
|
|||
static_assert_align(KernelCurveSegment, 8);
|
||||
|
||||
typedef struct KernelSpotLight {
|
||||
packed_float3 scaled_axis_u;
|
||||
float radius;
|
||||
packed_float3 scaled_axis_v;
|
||||
float eval_fac;
|
||||
packed_float3 dir;
|
||||
float radius;
|
||||
float eval_fac;
|
||||
float cos_half_spot_angle;
|
||||
float half_cot_half_spot_angle;
|
||||
float inv_len_z;
|
||||
float spot_smooth;
|
||||
int is_sphere;
|
||||
/* For non-uniform object scaling, the actual spread might be different. */
|
||||
float cos_half_larger_spread;
|
||||
} KernelSpotLight;
|
||||
|
||||
/* PointLight is SpotLight with only radius and invarea being used. */
|
||||
|
|
|
@ -1346,23 +1346,22 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
klights[light_index].area.normalize_spread = normalize_spread;
|
||||
}
|
||||
if (light->light_type == LIGHT_SPOT) {
|
||||
/* Scale axes to accommodate non-uniform scaling. */
|
||||
float3 scaled_axis_u = light->get_axisu() / len_squared(light->get_axisu());
|
||||
float3 scaled_axis_v = light->get_axisv() / len_squared(light->get_axisv());
|
||||
float len_z;
|
||||
/* Keep direction normalized. */
|
||||
float3 dir = safe_normalize_len(light->get_dir(), &len_z);
|
||||
const float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
|
||||
const float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
|
||||
const float tan_half_spot_angle = tanf(light->spot_angle * 0.5f);
|
||||
|
||||
float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
|
||||
float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
|
||||
const float len_w_sq = len_squared(light->get_dir());
|
||||
const float len_u_sq = len_squared(light->get_axisu());
|
||||
const float len_v_sq = len_squared(light->get_axisv());
|
||||
const float tan_sq = sqr(tan_half_spot_angle);
|
||||
|
||||
klights[light_index].spot.scaled_axis_u = scaled_axis_u;
|
||||
klights[light_index].spot.scaled_axis_v = scaled_axis_v;
|
||||
klights[light_index].spot.dir = dir;
|
||||
klights[light_index].spot.dir = safe_normalize(light->get_dir());
|
||||
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
|
||||
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tanf(light->spot_angle * 0.5f);
|
||||
klights[light_index].spot.inv_len_z = 1.0f / len_z;
|
||||
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tan_half_spot_angle;
|
||||
klights[light_index].spot.spot_smooth = spot_smooth;
|
||||
/* Choose the angle which spans a larger cone. */
|
||||
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
|
||||
1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq);
|
||||
}
|
||||
|
||||
klights[light_index].shader_id = shader_id;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d7827c77fe050a0e179fa0872c59dcbe71dc8595
|
||||
Subproject commit d983ed32a1e760130ba31ad8a98a94ea943267a2
|
|
@ -1 +1 @@
|
|||
Subproject commit 3d3ec946fc8808113204f6e8486c31ecee7dc804
|
||||
Subproject commit f1caad00e45d3741f84e5e68197a420f0819bded
|
|
@ -1 +1 @@
|
|||
Subproject commit b90554a2f217fff7204867f201a336b7bc52f6dc
|
||||
Subproject commit c0ef4a3e1bcac58868b9b8d0d0b65236f65d6219
|
|
@ -1 +1 @@
|
|||
Subproject commit c8dba5b39bfd6c25c789660b022034ff69062e7c
|
||||
Subproject commit a5521c85e03bfd1556ff1e63bf7163235c401497
|
|
@ -57,6 +57,7 @@ roles:
|
|||
displays:
|
||||
sRGB:
|
||||
- !<View> {name: Standard, colorspace: sRGB}
|
||||
- !<View> {name: Khronos PBR Neutral, colorspace: Khronos PBR Neutral sRGB}
|
||||
- !<View> {name: AgX, colorspace: AgX Base sRGB}
|
||||
- !<View> {name: Filmic, colorspace: Filmic sRGB}
|
||||
- !<View> {name: Filmic Log, colorspace: Filmic Log}
|
||||
|
@ -78,7 +79,7 @@ displays:
|
|||
- !<View> {name: False Color, colorspace: AgX False Color Rec.2020}
|
||||
- !<View> {name: Raw, colorspace: Non-Color}
|
||||
active_displays: [sRGB, Display P3, Rec.1886, Rec.2020]
|
||||
active_views: [Standard, AgX, Filmic, Filmic Log, False Color, Raw]
|
||||
active_views: [Standard, Khronos PBR Neutral, AgX, Filmic, Filmic Log, False Color, Raw]
|
||||
inactive_colorspaces: [Luminance Compensation Rec.2020, Luminance Compensation sRGB, Luminance Compensation P3, AgX False Color Rec.709, AgX False Color P3, AgX False Color Rec.1886, AgX False Color Rec.2020]
|
||||
|
||||
colorspaces:
|
||||
|
@ -485,6 +486,21 @@ colorspaces:
|
|||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: AgX False Color Rec.709}
|
||||
- !<ColorSpaceTransform> {src: Rec.1886, dst: Rec.2020}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Khronos PBR Neutral sRGB
|
||||
family: Khronos PBR Neutral
|
||||
equalitygroup:
|
||||
bitdepth: 32f
|
||||
description: |
|
||||
Khronos PBR Neutral Image Encoding for sRGB Display
|
||||
isdata: false
|
||||
from_scene_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear Rec.709}
|
||||
- !<AllocationTransform> {allocation: lg2, vars: [-9, 10]}
|
||||
- !<FileTransform> {src: pbrNeutral.cube, interpolation: tetrahedral}
|
||||
- !<ColorSpaceTransform> {src: Linear Rec.709, dst: sRGB}
|
||||
|
||||
looks:
|
||||
- !<Look>
|
||||
name: Very High Contrast
|
||||
|
@ -744,4 +760,4 @@ looks:
|
|||
style: log
|
||||
contrast: {rgb: [0.7, 0.7, 0.7], master: 1}
|
||||
saturation: 1.15
|
||||
pivot: {contrast: -0.2}
|
||||
pivot: {contrast: -0.2}
|
File diff suppressed because it is too large
Load Diff
|
@ -153,7 +153,7 @@ const UserDef U_default = {
|
|||
.pressure_softness = 0.0,
|
||||
.ndof_sensitivity = 4.0,
|
||||
.ndof_orbit_sensitivity = 4.0,
|
||||
.ndof_deadzone = 0.1,
|
||||
.ndof_deadzone = 0.0,
|
||||
.ndof_flag = (NDOF_MODE_ORBIT | NDOF_LOCK_HORIZON | NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM |
|
||||
NDOF_SHOULD_ROTATE |
|
||||
/* Software from the driver authors follows this convention
|
||||
|
|
|
@ -19,22 +19,24 @@ PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
------
|
||||
|
||||
** Audaspace; version 1.4+ (ae29ce2) -- https://audaspace.github.io/
|
||||
** Audaspace; version 0d18fe7 -- https://audaspace.github.io/
|
||||
** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew
|
||||
** Draco; version 1.3.6 -- https://google.github.io/draco/
|
||||
** Embree; version 4.1.0 -- https://github.com/embree/embree
|
||||
** Intel(R) oneAPI DPC++ compiler; version 2022-12 --
|
||||
https://github.com/intel/llvm#oneapi-dpc-compiler
|
||||
** Intel® Open Path Guiding Library; version 0.6.0 -- http://www.openpgl.org/
|
||||
** Intel® Open Path Guiding Library; version 0.5.0 -- http://www.openpgl.org/
|
||||
** Mantaflow; version 0.13 -- http://mantaflow.com/
|
||||
** materialX; version 1.38.6 --
|
||||
** materialX; version 1.38.8 --
|
||||
https://github.com/AcademySoftwareFoundation/MaterialX
|
||||
** meson; version 0.63 -- https://github.com/mesonbuild/meson
|
||||
** oneAPI Threading Building Block; version 2020_U3 --
|
||||
https://software.intel.com/en-us/oneapi/onetbb
|
||||
** OpenImageDenoise; version 1.4.3 -- https://www.openimagedenoise.org/
|
||||
** OpenImageIO; version 2.4.15.0 -- http://www.openimageio.org
|
||||
** OpenSSL; version 3.1.2 -- https://www.openssl.org/
|
||||
** OpenCL Wrangler; version 27a6867 -- https://github.com/OpenCLWrangler/clew
|
||||
** OpenImageDenoise; version 2.2.2 -- https://www.openimagedenoise.org/
|
||||
** OpenImageIO; version 2.5.6.0 --
|
||||
https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
** OpenSSL; version 3.1.5 -- https://www.openssl.org/
|
||||
** OpenXR SDK; version 1.0.17 -- https://khronos.org/openxr
|
||||
** RangeTree; version 40ebed8aa209 -- https://github.com/ideasman42/rangetree-c
|
||||
** SDL Extension Wrangler; version 15edf8e --
|
||||
|
@ -42,7 +44,9 @@ https://github.com/SDLWrangler/sdlew
|
|||
** ShaderC; version 2022.3 -- https://github.com/google/shaderc
|
||||
** SYCL Unified Runtime ; version fd711c920acc4434cb52ff18b078c082d9d7f44d --
|
||||
https://github.com/oneapi-src/unified-runtime
|
||||
** Vulkan Loader; version 1.2.198 --
|
||||
** Vulkan Headers; version 1.3.270 --
|
||||
https://github.com/KhronosGroup/Vulkan-Headers
|
||||
** Vulkan Loader; version 1.3.270 --
|
||||
https://github.com/KhronosGroup/Vulkan-Loader
|
||||
|
||||
Apache License
|
||||
|
@ -244,7 +248,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
|
||||
* For Audaspace see also this required NOTICE:
|
||||
Copyright © 2009-2023 Jörg Müller. All rights reserved.
|
||||
Copyright © 2009-2020 Jörg Müller. All rights reserved.
|
||||
* For Cuda Wrangler see also this required NOTICE:
|
||||
Copyright 2011-2014 Blender Foundation
|
||||
* For Draco see also this required NOTICE:
|
||||
|
@ -290,6 +294,8 @@ limitations under the License.
|
|||
Copyright 2015 The Shaderc Authors. All rights reserved.
|
||||
* For SYCL Unified Runtime see also this required NOTICE:
|
||||
Copyright (C) 2022-2023 Intel Corporation
|
||||
* For Vulkan Headers see also this required NOTICE:
|
||||
Copyright 2015-2023 The Khronos Group Inc.
|
||||
* For Vulkan Loader see also this required NOTICE:
|
||||
Copyright (c) 2019 The Khronos Group Inc.
|
||||
Copyright (c) 2019 Valve Corporation
|
||||
|
@ -391,22 +397,21 @@ Copyright (c) 2006, Google Inc.
|
|||
All rights reserved.
|
||||
** Imath; version 3.1.7 -- https://github.com/AcademySoftwareFoundation/Imath
|
||||
Copyright Contributors to the OpenEXR Project. All rights reserved.
|
||||
** ISPC; version 1.17.0 -- https://github.com/ispc/ispc
|
||||
** ISPC; version 1.21.1 -- https://github.com/ispc/ispc
|
||||
Copyright Intel Corporation
|
||||
All rights reserved.
|
||||
** NumPy; version 1.23.5 -- https://numpy.org/
|
||||
** NumPy; version 1.24.3 -- https://numpy.org/
|
||||
Copyright (c) 2005-2022, NumPy Developers.
|
||||
All rights reserved.
|
||||
** Ogg; version 1.3.5 -- https://www.xiph.org/ogg/
|
||||
COPYRIGHT (C) 1994-2019 by the Xiph.Org Foundation https://www.xiph.org/
|
||||
** Open Shading Language; version
|
||||
1.13-dev-1a7670600c8b08c2443a78d03c8c27e9a1149140 --
|
||||
https://github.com/imageworks/OpenShadingLanguage
|
||||
** Open Shading Language; version 1.13.7.0 --
|
||||
https://github.com/AcademySoftwareFoundation/OpenShadingLanguage/
|
||||
Copyright Contributors to the Open Shading Language project.
|
||||
** OpenColorIO; version 2.2.0 --
|
||||
** OpenColorIO; version 2.3.2 --
|
||||
https://github.com/AcademySoftwareFoundation/OpenColorIO
|
||||
Copyright Contributors to the OpenColorIO Project.
|
||||
** OpenEXR; version 3.1.7 --
|
||||
** OpenEXR; version 3.2.1 --
|
||||
https://github.com/AcademySoftwareFoundation/openexr
|
||||
Copyright Contributors to the OpenEXR Project. All rights reserved.
|
||||
** Pystring; version 1.1.3 -- https://github.com/imageworks/pystring
|
||||
|
@ -414,7 +419,7 @@ Copyright (c) 2008-2010, Sony Pictures Imageworks Inc
|
|||
All rights reserved.
|
||||
** Vorbis; version 1.3.7 -- https://xiph.org/vorbis/
|
||||
Copyright (c) 2002-2020 Xiph.org Foundation
|
||||
** VPX; version 1.11.0 -- https://github.com/webmproject/libvpx
|
||||
** VPX; version 1.14.0 -- https://github.com/webmproject/libvpx
|
||||
Copyright (c) 2010, The WebM Project authors. All rights reserved.
|
||||
** WebP; version 1.3.2 -- https://github.com/webmproject/libwebp
|
||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
@ -913,7 +918,7 @@ Copyright (c) 2003, 2007-14 Matteo Frigo
|
|||
Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
|
||||
** GMP; version 6.2.1 -- https://gmplib.org/
|
||||
Copyright 1996-2020 Free Software Foundation, Inc.
|
||||
** OpenAL; version 1.21.1 -- http://openal-soft.org
|
||||
** OpenAL; version 1.23.1 -- http://openal-soft.org
|
||||
Copyright (c) 2015, Archontis Politis
|
||||
Copyright (c) 2019, Christopher Robinson
|
||||
|
||||
|
@ -2804,7 +2809,7 @@ That's all there is to it!
|
|||
|
||||
------
|
||||
|
||||
** FFmpeg; version 6.0 -- http://ffmpeg.org/
|
||||
** FFmpeg; version 6.1.1 -- http://ffmpeg.org/
|
||||
Copyright: The FFmpeg contributors
|
||||
https://github.com/FFmpeg/FFmpeg/blob/master/CREDITS
|
||||
** Libsndfile; version 1.2.2 -- http://libsndfile.github.io/libsndfile/
|
||||
|
@ -3433,6 +3438,33 @@ December 9, 2010
|
|||
|
||||
------
|
||||
|
||||
** libdeflate; version 1.18 -- https://github.com/ebiggers/libdeflate
|
||||
Copyright 2016 Eric Biggers
|
||||
|
||||
Copyright 2016 Eric Biggers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
------
|
||||
|
||||
** vcintrinsics; version 782fbf7301dc73acaa049a4324c976ad94f587f7 --
|
||||
https://github.com/intel/vc-intrinsics
|
||||
Copyright (c) 2019 Intel Corporation
|
||||
|
@ -3471,7 +3503,7 @@ Copyright © 2013 The Khronos Group Inc.
|
|||
** Expat; version 2.5.0 -- https://github.com/libexpat/libexpat/
|
||||
Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
|
||||
Copyright (c) 2001-2019 Expat maintainers
|
||||
** Intel(R) Graphics Compute Runtime; version 22.38.24278 --
|
||||
** Intel(R) Graphics Compute Runtime; version 23.43.27642.40 --
|
||||
https://github.com/intel/compute-runtime
|
||||
Copyright (C) 2021 Intel Corporation
|
||||
** Intel(R) Graphics Memory Management Library; version 22.1.8 --
|
||||
|
@ -3490,11 +3522,11 @@ Copyright (c) 2006, 2008 Junio C Hamano
|
|||
Copyright © 2017-2018 Red Hat Inc.
|
||||
Copyright © 2012 Collabora, Ltd.
|
||||
Copyright © 2008 Kristian Høgsberg
|
||||
** Libxml2; version 2.10.4 -- http://xmlsoft.org/
|
||||
** Libxml2; version 2.12.3 -- http://xmlsoft.org/
|
||||
Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved.
|
||||
** Mesa 3D; version 21.1.5 -- https://www.mesa3d.org/
|
||||
** Mesa 3D; version 23.3.0 -- https://www.mesa3d.org/
|
||||
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
|
||||
** oneAPI Level Zero; version v1.8.8 --
|
||||
** oneAPI Level Zero; version v1.15.8 --
|
||||
https://github.com/oneapi-src/level-zero
|
||||
Copyright (C) 2019-2021 Intel Corporation
|
||||
** OPENCollada; version 1.6.68 -- https://github.com/KhronosGroup/OpenCOLLADA
|
||||
|
@ -3506,11 +3538,12 @@ Copyright (c) 2018 Jingwei Huang, Yichao Zhou, Matthias Niessner,
|
|||
Jonathan Shewchuk and Leonidas Guibas. All rights reserved.
|
||||
** robin-map; version 0.6.2 -- https://github.com/Tessil/robin-map
|
||||
Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
** sse2neon; version 1.6.0 -- https://github.com/DLTcollab/sse2neon
|
||||
** sse2neon; version cfaa59fc04fecb117c0a0f3fe9c82dece6f359ad --
|
||||
https://github.com/DLTcollab/sse2neon
|
||||
Copyright sse2neon contributors
|
||||
** TinyGLTF; version 2.8.21 -- https://github.com/syoyo/tinygltf
|
||||
** TinyGLTF; version 2.5.0 -- https://github.com/syoyo/tinygltf
|
||||
Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors
|
||||
** Wayland protocols; version 1.31 --
|
||||
** Wayland protocols; version 1.32 --
|
||||
https://gitlab.freedesktop.org/wayland/wayland-protocols
|
||||
Copyright © 2008-2013 Kristian Høgsberg
|
||||
Copyright © 2010-2013 Intel Corporation
|
||||
|
@ -3605,7 +3638,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|||
|
||||
------
|
||||
|
||||
** OpenVDB; version 10.0.0 -- http://www.openvdb.org/
|
||||
** OpenVDB; version 11.0.0 -- http://www.openvdb.org/
|
||||
Copyright Contributors to the OpenVDB Project
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
|
@ -4037,7 +4070,7 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
------
|
||||
|
||||
** The LLVM Compiler Infrastructure; version 12.0.0 --
|
||||
** The LLVM Compiler Infrastructure; version 17.0.6 --
|
||||
https://github.com/llvm/llvm-project/
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
@ -4063,9 +4096,9 @@ Software.
|
|||
|
||||
------
|
||||
|
||||
** OpenSubdiv; version 3.5.0 -- http://graphics.pixar.com/opensubdiv
|
||||
** OpenSubdiv; version 3.6.0 -- http://graphics.pixar.com/opensubdiv
|
||||
Copyright 2013 Pixar
|
||||
** Universal Scene Description; version 23.05 -- http://www.openusd.org/
|
||||
** Universal Scene Description; version 23.11 -- http://www.openusd.org/
|
||||
Copyright 2016 Pixar
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "Apache License") with the
|
||||
|
@ -4110,7 +4143,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
------
|
||||
|
||||
** Boost C++ Libraries; version 1.80.0 -- https://www.boost.org/
|
||||
** Boost C++ Libraries; version 1.82.0 -- https://www.boost.org/
|
||||
The Boost license encourages both commercial and non-commercial use and does
|
||||
not require attribution for binary use.
|
||||
|
||||
|
@ -4276,7 +4309,7 @@ MIT Expat
|
|||
|
||||
------
|
||||
|
||||
** Python; version 3.10.13 -- https://www.python.org
|
||||
** Python; version 3.11.7 -- https://www.python.org
|
||||
Copyright (c) 2001-2021 Python Software Foundation. All rights reserved.
|
||||
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
|
@ -4563,6 +4596,33 @@ PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
------
|
||||
|
||||
** libffi; version 3.4.4 -- https://github.com/libffi/libffi/
|
||||
libffi - Copyright (c) 1996-2024 Anthony Green, Red Hat, Inc and others.
|
||||
|
||||
libffi - Copyright (c) 1996-2024 Anthony Green, Red Hat, Inc and others.
|
||||
See source files for details.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
``Software''), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
------
|
||||
|
||||
** Flex; version 2.6.4 -- https://github.com/westes/flex
|
||||
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The Flex Project.
|
||||
|
||||
|
|
|
@ -319,14 +319,29 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
is_extension = module_name.startswith(_ext_base_pkg_idname_with_dot)
|
||||
|
||||
if handle_error is None:
|
||||
def handle_error(_ex):
|
||||
def handle_error(ex):
|
||||
if isinstance(ex, ImportError):
|
||||
# NOTE: checking "Add-on " prefix is rather weak,
|
||||
# it's just a way to avoid the noise of a full trace-back when
|
||||
# an add-on is simply missing on the file-system.
|
||||
if (type(msg := ex.msg) is str) and msg.startswith("Add-on "):
|
||||
print(msg)
|
||||
return
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# reload if the mtime changes
|
||||
mod = sys.modules.get(module_name)
|
||||
# chances of the file _not_ existing are low, but it could be removed
|
||||
if mod and os.path.exists(mod.__file__):
|
||||
|
||||
# Set to `mod.__file__` or None.
|
||||
mod_file = None
|
||||
|
||||
if (
|
||||
(mod is not None) and
|
||||
(mod_file := mod.__file__) is not None and
|
||||
os.path.exists(mod_file)
|
||||
):
|
||||
|
||||
if getattr(mod, "__addon_enabled__", False):
|
||||
# This is an unlikely situation,
|
||||
|
@ -336,18 +351,15 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
try:
|
||||
mod.unregister()
|
||||
except BaseException as ex:
|
||||
print(
|
||||
"Exception in module unregister():",
|
||||
repr(getattr(mod, "__file__", module_name)),
|
||||
)
|
||||
print("Exception in module unregister():", (mod_file or module_name))
|
||||
handle_error(ex)
|
||||
return None
|
||||
|
||||
mod.__addon_enabled__ = False
|
||||
mtime_orig = getattr(mod, "__time__", 0)
|
||||
mtime_new = os.path.getmtime(mod.__file__)
|
||||
mtime_new = os.path.getmtime(mod_file)
|
||||
if mtime_orig != mtime_new:
|
||||
print("module changed on disk:", repr(mod.__file__), "reloading...")
|
||||
print("module changed on disk:", repr(mod_file), "reloading...")
|
||||
|
||||
try:
|
||||
importlib.reload(mod)
|
||||
|
@ -374,11 +386,20 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
# Use instead of `__import__` so that sub-modules can eventually be supported.
|
||||
# This is also documented to be the preferred way to import modules.
|
||||
mod = importlib.import_module(module_name)
|
||||
if mod.__file__ is None:
|
||||
# This can happen when the addon has been removed but there are
|
||||
# residual `.pyc` files left behind.
|
||||
raise ImportError(name=module_name)
|
||||
mod.__time__ = os.path.getmtime(mod.__file__)
|
||||
if (mod_file := mod.__file__) is None:
|
||||
# This can happen when:
|
||||
# - The add-on has been removed but there are residual `.pyc` files left behind.
|
||||
# - An extension is a directory that doesn't contain an `__init__.py` file.
|
||||
#
|
||||
# Include a message otherwise the "cause:" for failing to load the module is left blank.
|
||||
# Include the `__path__` when available so there is a reference to the location that failed to load.
|
||||
raise ImportError(
|
||||
"module loaded with no associated file, __path__=%r, aborting!" % (
|
||||
getattr(mod, "__path__", None)
|
||||
),
|
||||
name=module_name
|
||||
)
|
||||
mod.__time__ = os.path.getmtime(mod_file)
|
||||
mod.__addon_enabled__ = False
|
||||
except BaseException as ex:
|
||||
# If the add-on doesn't exist, don't print full trace-back because the back-trace is in this case
|
||||
|
@ -386,7 +407,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
# Account for `ImportError` & `ModuleNotFoundError`.
|
||||
if isinstance(ex, ImportError):
|
||||
if ex.name == module_name:
|
||||
print("Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex)))
|
||||
ex.msg = "Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex))
|
||||
|
||||
# Issue with an add-on from an extension repository, report a useful message.
|
||||
elif is_extension and module_name.startswith(ex.name + "."):
|
||||
|
@ -396,22 +417,20 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
None,
|
||||
)
|
||||
if repo is None:
|
||||
print(
|
||||
ex.msg = (
|
||||
"Add-on not loaded: \"%s\", cause: extension repository \"%s\" doesn't exist" %
|
||||
(module_name, repo_id)
|
||||
)
|
||||
elif not repo.enabled:
|
||||
print(
|
||||
ex.msg = (
|
||||
"Add-on not loaded: \"%s\", cause: extension repository \"%s\" is disabled" %
|
||||
(module_name, repo_id)
|
||||
)
|
||||
else:
|
||||
# The repository exists and is enabled, it should have imported.
|
||||
print("Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex)))
|
||||
else:
|
||||
handle_error(ex)
|
||||
else:
|
||||
handle_error(ex)
|
||||
ex.msg = "Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex))
|
||||
|
||||
handle_error(ex)
|
||||
|
||||
if default_set:
|
||||
_addon_remove(module_name)
|
||||
|
@ -425,10 +444,13 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
if bl_info is not None:
|
||||
# Use `_init` to detect when `bl_info` was generated from the manifest, see: `_bl_info_from_extension`.
|
||||
if type(bl_info) is dict and "_init" not in bl_info:
|
||||
print(
|
||||
"Add-on \"%s\" has a \"bl_info\" which will be ignored in favor of \"%s\"" %
|
||||
(module_name, _ext_manifest_filename_toml)
|
||||
)
|
||||
# This print is noisy, hide behind a debug flag.
|
||||
# Once `bl_info` is fully deprecated this should be changed to always print a warning.
|
||||
if _bpy.app.debug_python:
|
||||
print(
|
||||
"Add-on \"%s\" has a \"bl_info\" which will be ignored in favor of \"%s\"" %
|
||||
(module_name, _ext_manifest_filename_toml)
|
||||
)
|
||||
# Always remove as this is not expected to exist and will be lazily initialized.
|
||||
del mod.bl_info
|
||||
|
||||
|
@ -443,10 +465,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
try:
|
||||
mod.register()
|
||||
except BaseException as ex:
|
||||
print(
|
||||
"Exception in module register():",
|
||||
getattr(mod, "__file__", module_name),
|
||||
)
|
||||
print("Exception in module register():", (mod_file or module_name))
|
||||
handle_error(ex)
|
||||
del sys.modules[module_name]
|
||||
if default_set:
|
||||
|
@ -490,7 +509,7 @@ def disable(module_name, *, default_set=False, handle_error=None):
|
|||
# the add-on in the user preferences.
|
||||
if mod and getattr(mod, "__addon_enabled__", False) is not False:
|
||||
mod.__addon_enabled__ = False
|
||||
mod.__addon_persistent = False
|
||||
mod.__addon_persistent__ = False
|
||||
|
||||
try:
|
||||
mod.unregister()
|
||||
|
@ -503,7 +522,7 @@ def disable(module_name, *, default_set=False, handle_error=None):
|
|||
print(
|
||||
"addon_utils.disable: %s not %s" % (
|
||||
module_name,
|
||||
"disabled" if mod is None else "loaded")
|
||||
"loaded" if mod is None else "enabled")
|
||||
)
|
||||
|
||||
# could be in more than once, unlikely but better do this just in case.
|
||||
|
|
|
@ -10,6 +10,7 @@ __all__ = (
|
|||
"app",
|
||||
"context",
|
||||
"data",
|
||||
"msgbus",
|
||||
"ops",
|
||||
"path",
|
||||
"props",
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
__all__ = (
|
||||
"ToolDef",
|
||||
)
|
||||
|
||||
# Until we untangle ToolDef from bl_ui internals,
|
||||
# use this module to document ToolDef.
|
||||
from bl_ui.space_toolsystem_common import ToolDef
|
||||
|
|
|
@ -181,7 +181,7 @@ def write_sysinfo(filepath):
|
|||
output.write("SDL: Blender was built without SDL support\n")
|
||||
|
||||
if bpy.app.background:
|
||||
output.write("\nOpenGL: missing, background mode\n")
|
||||
output.write("\nGPU: missing, background mode\n")
|
||||
else:
|
||||
output.write(title("GPU"))
|
||||
output.write("renderer:\t%r\n" % gpu.platform.renderer_get())
|
||||
|
|
|
@ -529,6 +529,15 @@ class ARMATURE_OT_copy_bone_color_to_selected(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
def _armature_from_context(context):
|
||||
pin_armature = getattr(context, 'armature', None)
|
||||
if pin_armature:
|
||||
return pin_armature
|
||||
if context.object and context.object.type == 'ARMATURE':
|
||||
return context.object.data
|
||||
return None
|
||||
|
||||
|
||||
class ARMATURE_OT_collection_show_all(Operator):
|
||||
"""Show all bone collections"""
|
||||
bl_idname = "armature.collection_show_all"
|
||||
|
@ -537,10 +546,10 @@ class ARMATURE_OT_collection_show_all(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'ARMATURE' and context.object.data
|
||||
return _armature_from_context(context) is not None
|
||||
|
||||
def execute(self, context):
|
||||
arm = context.object.data
|
||||
arm = _armature_from_context(context)
|
||||
for bcoll in arm.collections_all:
|
||||
bcoll.is_visible = True
|
||||
return {'FINISHED'}
|
||||
|
@ -554,15 +563,16 @@ class ARMATURE_OT_collection_unsolo_all(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not (context.object and context.object.type == 'ARMATURE' and context.object.data):
|
||||
armature = _armature_from_context(context)
|
||||
if not armature:
|
||||
return False
|
||||
if not context.object.data.collections.is_solo_active:
|
||||
if not armature.collections.is_solo_active:
|
||||
cls.poll_message_set("None of the bone collections is marked 'solo'")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
arm = context.object.data
|
||||
arm = _armature_from_context(context)
|
||||
for bcoll in arm.collections_all:
|
||||
bcoll.is_solo = False
|
||||
return {'FINISHED'}
|
||||
|
@ -578,16 +588,16 @@ class ARMATURE_OT_collection_remove_unused(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.object or context.object.type != 'ARMATURE':
|
||||
armature = _armature_from_context(context)
|
||||
if not armature:
|
||||
return False
|
||||
arm = context.object.data
|
||||
return len(arm.collections) > 0
|
||||
return len(armature.collections) > 0
|
||||
|
||||
def execute(self, context):
|
||||
if context.object.mode == 'EDIT':
|
||||
if context.mode == 'EDIT_ARMATURE':
|
||||
return self.execute_edit_mode(context)
|
||||
|
||||
armature = context.object.data
|
||||
armature = _armature_from_context(context)
|
||||
|
||||
# Build a set of bone collections that don't contain any bones, and
|
||||
# whose children also don't contain any bones.
|
||||
|
@ -608,7 +618,7 @@ class ARMATURE_OT_collection_remove_unused(Operator):
|
|||
# edit mode, because that has a completely separate list of edit bones.
|
||||
# This is why edit mode needs separate handling.
|
||||
|
||||
armature = context.object.data
|
||||
armature = _armature_from_context(context)
|
||||
bcolls_with_bones = {
|
||||
bcoll
|
||||
for ebone in armature.edit_bones
|
||||
|
|
|
@ -443,9 +443,13 @@ class PREFERENCES_OT_addon_enable(Operator):
|
|||
|
||||
def err_cb(ex):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# The full trace-back in the UI is unwieldy and associated with unhandled exceptions.
|
||||
# Only show a single exception instead of the full trace-back,
|
||||
# developers can debug using information printed in the console.
|
||||
nonlocal err_str
|
||||
err_str = traceback.format_exc()
|
||||
print(err_str)
|
||||
err_str = str(ex)
|
||||
|
||||
mod = addon_utils.enable(self.module, default_set=True, handle_error=err_cb)
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
|
|||
col.prop(light, "use_shadow", text="Cast Shadow")
|
||||
col.prop(light, "shadow_softness_factor", text="Shadow Softness")
|
||||
col.prop(light, "shadow_filter_radius", text="Filtering Radius")
|
||||
col.prop(light, "shadow_resolution_scale", text="Resolution Scale")
|
||||
|
||||
if light.type == 'SUN':
|
||||
col.prop(light, "shadow_trace_distance", text="Trace Distance")
|
||||
|
|
|
@ -155,6 +155,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
|
|||
self.operator_modifier_add(layout, 'WIREFRAME')
|
||||
if ob_type == 'GREASEPENCIL':
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARRAY')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_BUILD')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_ENVELOPE')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')
|
||||
|
|
|
@ -113,7 +113,8 @@ class RENDER_PT_color_management_display_settings(RenderButtonsPanel, Panel):
|
|||
col = layout.column(align=True)
|
||||
sub = col.row()
|
||||
sub.active = (not view.view_transform.startswith("Filmic") and not view.view_transform.startswith("AgX") and not
|
||||
view.view_transform.startswith("False Color"))
|
||||
view.view_transform.startswith("False Color") and not
|
||||
view.view_transform.startswith("Khronos PBR Neutral"))
|
||||
sub.prop(view, "use_hdr_view")
|
||||
|
||||
|
||||
|
@ -197,6 +198,7 @@ class RENDER_PT_eevee_next_horizon_scan(RenderButtonsPanel, Panel):
|
|||
col.prop(props, "horizon_quality", text="Precision")
|
||||
col.prop(props, "horizon_thickness", text="Thickness")
|
||||
col.prop(props, "horizon_bias", text="Bias")
|
||||
col.prop(props, "horizon_resolution", text="Resolution")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):
|
||||
|
@ -725,6 +727,9 @@ class RENDER_PT_eevee_next_shadows(RenderButtonsPanel, Panel):
|
|||
col = layout.column()
|
||||
col.prop(props, "shadow_normal_bias", text="Normal Bias")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(props, "use_shadow_jittered_viewport", text="Jittered Transparency (Viewport)")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel):
|
||||
bl_label = "Sampling"
|
||||
|
|
|
@ -121,7 +121,7 @@ class EEVEE_WORLD_PT_volume(WorldButtonsPanel, Panel):
|
|||
bl_label = "Volume"
|
||||
bl_translation_context = i18n_contexts.id_id
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
|
|
@ -2251,8 +2251,11 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
|
|||
|
||||
prefs = context.preferences
|
||||
|
||||
use_extension_repos = prefs.experimental.use_extension_repos
|
||||
if use_extension_repos and self.is_extended():
|
||||
if (
|
||||
prefs.view.show_developer_ui and
|
||||
prefs.experimental.use_extension_repos and
|
||||
self.is_extended()
|
||||
):
|
||||
# Rely on the draw function being appended to by the extensions add-on.
|
||||
return
|
||||
|
||||
|
@ -2695,6 +2698,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
|
|||
({"property": "use_new_matrix_socket"}, ("blender/blender/issues/116067", "Matrix Socket")),
|
||||
({"property": "enable_overlay_next"}, ("blender/blender/issues/102179", "#102179")),
|
||||
({"property": "use_extension_repos"}, ("/blender/blender/issues/117286", "#117286")),
|
||||
({"property": "use_extension_utils"}, ("/blender/blender/issues/117286", "#117286")),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -2678,7 +2678,7 @@ class VIEW3D_MT_image_add(Menu):
|
|||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
# Expliclitly set background mode on/off as operator will try to
|
||||
# Explicitly set background mode on/off as operator will try to
|
||||
# auto detect which mode to use otherwise.
|
||||
layout.operator("object.empty_image_add", text="Reference", icon='IMAGE_REFERENCE').background = False
|
||||
layout.operator("object.empty_image_add", text="Background", icon='IMAGE_BACKGROUND').background = True
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_math_color_blend.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_path_util.h"
|
||||
|
@ -344,6 +345,12 @@ void blf_batch_draw()
|
|||
|
||||
GPU_batch_program_set_builtin(g_batch.batch, GPU_SHADER_TEXT);
|
||||
GPU_batch_texture_bind(g_batch.batch, "glyph", texture);
|
||||
/* Setup texture width mask and shift, so that shader can avoid costly divisions. */
|
||||
int tex_width = GPU_texture_width(texture);
|
||||
BLI_assert_msg(is_power_of_2_i(tex_width), "Font texture width must be power of two");
|
||||
int width_shift = 31 - bitscan_reverse_i(tex_width);
|
||||
GPU_batch_uniform_1i(g_batch.batch, "glyph_tex_width_mask", tex_width - 1);
|
||||
GPU_batch_uniform_1i(g_batch.batch, "glyph_tex_width_shift", width_shift);
|
||||
GPU_batch_draw(g_batch.batch);
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 9
|
||||
#define BLENDER_FILE_SUBVERSION 11
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -110,7 +110,7 @@ enum eCbEvent {
|
|||
BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST,
|
||||
BKE_CB_EVT_EXTENSION_REPOS_SYNC,
|
||||
BKE_CB_EVT_EXTENSION_REPOS_UPGRADE,
|
||||
BKE_CB_EVT_EXTENSION_DROP_URL,
|
||||
BKE_CB_EVT_EXTENSION_REPOS_FILES_CLEAR,
|
||||
BKE_CB_EVT_TOT,
|
||||
};
|
||||
|
||||
|
|
|
@ -86,11 +86,11 @@ char BKE_imtype_valid_depths(char imtype);
|
|||
|
||||
/**
|
||||
* String is from command line `--render-format` argument,
|
||||
* keep in sync with `creator_args.c` help info.
|
||||
* keep in sync with `creator_args.cc` help info.
|
||||
*/
|
||||
char BKE_imtype_from_arg(const char *imtype_arg);
|
||||
|
||||
/* Conversion between ImBuf settings. */
|
||||
/* Conversion between #ImBuf settings. */
|
||||
|
||||
void BKE_image_format_from_imbuf(struct ImageFormatData *im_format, const struct ImBuf *imbuf);
|
||||
void BKE_image_format_to_imbuf(struct ImBuf *ibuf, const struct ImageFormatData *imf);
|
||||
|
|
|
@ -394,6 +394,12 @@ struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt);
|
|||
* Make the given NLA-Strip the active one within the given block.
|
||||
*/
|
||||
void BKE_nlastrip_set_active(struct AnimData *adt, struct NlaStrip *strip);
|
||||
/**
|
||||
* Find the NLA-strip with the given name within the given track.
|
||||
*
|
||||
* \return pointer to the strip, or nullptr when not found.
|
||||
*/
|
||||
struct NlaStrip *BKE_nlastrip_find_by_name(struct NlaTrack *nlt, const char *name);
|
||||
|
||||
/**
|
||||
* Does the given NLA-strip fall within the given bounds (times)?.
|
||||
|
|
|
@ -112,7 +112,7 @@ void BKE_scene_object_base_flag_sync_from_base(Base *base);
|
|||
*/
|
||||
void BKE_scene_set_background(Main *bmain, Scene *sce);
|
||||
/**
|
||||
* Called from `creator_args.c`.
|
||||
* Called from `creator_args.cc`.
|
||||
*/
|
||||
Scene *BKE_scene_set_name(Main *bmain, const char *name);
|
||||
|
||||
|
|
|
@ -1148,6 +1148,9 @@ static void editbmesh_calc_modifiers(Depsgraph *depsgraph,
|
|||
mesh_final->edit_mesh->is_shallow_copy = true;
|
||||
mesh_final->runtime->is_original_bmesh = true;
|
||||
BKE_mesh_runtime_ensure_edit_data(mesh_final);
|
||||
if (!mesh_cage->runtime->edit_data->vertexCos.is_empty()) {
|
||||
mesh_final->runtime->edit_data->vertexCos = mesh_cage->runtime->edit_data->vertexCos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1495,6 +1495,7 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re
|
|||
|
||||
if ((lapp_context->params->flag & FILE_LINK) != 0) {
|
||||
blendfile_link_append_proxies_convert(lapp_context->params->bmain, reports);
|
||||
BKE_main_mesh_legacy_convert_auto_smooth(*lapp_context->params->bmain);
|
||||
}
|
||||
|
||||
BKE_main_namemap_clear(lapp_context->params->bmain);
|
||||
|
|
|
@ -244,6 +244,9 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
|
|||
const T default_value = T())
|
||||
{
|
||||
const int num = domain_num(curves, domain);
|
||||
if (num <= 0) {
|
||||
return {};
|
||||
}
|
||||
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
|
||||
CustomData &custom_data = domain_custom_data(curves, domain);
|
||||
|
||||
|
|
|
@ -604,11 +604,16 @@ static float dvar_eval_transChan(const AnimationEvalContext * /*anim_eval_contex
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Target should be valid now. */
|
||||
dtar->flag &= ~DTAR_FLAG_INVALID;
|
||||
|
||||
/* Try to get pose-channel. */
|
||||
pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
|
||||
if (dtar->pchan_name[0] != '\0' && !pchan) {
|
||||
driver->flag |= DRIVER_FLAG_INVALID;
|
||||
dtar->flag |= DTAR_FLAG_INVALID;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Target should be valid now. */
|
||||
dtar->flag &= ~DTAR_FLAG_INVALID;
|
||||
|
||||
/* Check if object or bone, and get transform matrix accordingly:
|
||||
* - "use_eulers" code is used to prevent the problems associated with non-uniqueness
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "BKE_object_types.hh"
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_euler_types.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
|
@ -317,10 +318,18 @@ Drawing::~Drawing()
|
|||
|
||||
Span<uint3> Drawing::triangles() const
|
||||
{
|
||||
const char *func = __func__;
|
||||
this->runtime->triangles_cache.ensure([&](Vector<uint3> &r_data) {
|
||||
MemArena *pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, func);
|
||||
struct LocalMemArena {
|
||||
MemArena *pf_arena = nullptr;
|
||||
LocalMemArena() : pf_arena(BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "Drawing::triangles")) {}
|
||||
|
||||
~LocalMemArena()
|
||||
{
|
||||
if (pf_arena != nullptr) {
|
||||
BLI_memarena_free(pf_arena);
|
||||
}
|
||||
}
|
||||
};
|
||||
this->runtime->triangles_cache.ensure([&](Vector<uint3> &r_data) {
|
||||
const CurvesGeometry &curves = this->strokes();
|
||||
const Span<float3> positions = curves.positions();
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
|
@ -336,36 +345,37 @@ Span<uint3> Drawing::triangles() const
|
|||
}
|
||||
|
||||
r_data.resize(total_triangles);
|
||||
MutableSpan<uint3> triangles = r_data.as_mutable_span();
|
||||
threading::EnumerableThreadSpecific<LocalMemArena> all_local_mem_arenas;
|
||||
threading::parallel_for(curves.curves_range(), 32, [&](const IndexRange range) {
|
||||
MemArena *pf_arena = all_local_mem_arenas.local().pf_arena;
|
||||
for (const int curve_i : range) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: use threading. */
|
||||
for (const int curve_i : curves.curves_range()) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const int num_triangles = points.size() - 2;
|
||||
MutableSpan<uint3> r_tris = triangles.slice(tris_offests[curve_i], num_triangles);
|
||||
|
||||
if (points.size() < 3) {
|
||||
continue;
|
||||
float(*projverts)[2] = static_cast<float(*)[2]>(
|
||||
BLI_memarena_alloc(pf_arena, sizeof(*projverts) * size_t(points.size())));
|
||||
|
||||
float3x3 axis_mat;
|
||||
axis_dominant_v3_to_m3(axis_mat.ptr(), float3(0.0f, -1.0f, 0.0f));
|
||||
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
mul_v2_m3v3(projverts[i], axis_mat.ptr(), positions[points[i]]);
|
||||
}
|
||||
|
||||
BLI_polyfill_calc_arena(projverts,
|
||||
points.size(),
|
||||
0,
|
||||
reinterpret_cast<uint32_t(*)[3]>(r_tris.data()),
|
||||
pf_arena);
|
||||
BLI_memarena_clear(pf_arena);
|
||||
}
|
||||
|
||||
const int num_triangles = points.size() - 2;
|
||||
MutableSpan<uint3> r_tris = r_data.as_mutable_span().slice(tris_offests[curve_i],
|
||||
num_triangles);
|
||||
|
||||
float(*projverts)[2] = static_cast<float(*)[2]>(
|
||||
BLI_memarena_alloc(pf_arena, sizeof(*projverts) * size_t(points.size())));
|
||||
|
||||
/* TODO: calculate axis_mat properly. */
|
||||
float3x3 axis_mat;
|
||||
axis_dominant_v3_to_m3(axis_mat.ptr(), float3(0.0f, -1.0f, 0.0f));
|
||||
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
mul_v2_m3v3(projverts[i], axis_mat.ptr(), positions[points[i]]);
|
||||
}
|
||||
|
||||
BLI_polyfill_calc_arena(
|
||||
projverts, points.size(), 0, reinterpret_cast<uint32_t(*)[3]>(r_tris.data()), pf_arena);
|
||||
BLI_memarena_clear(pf_arena);
|
||||
}
|
||||
|
||||
BLI_memarena_free(pf_arena);
|
||||
});
|
||||
});
|
||||
|
||||
return this->runtime->triangles_cache.data().as_span();
|
||||
|
|
|
@ -1737,6 +1737,97 @@ static void legacy_object_modifier_weight_lineart(Object &object, GpencilModifie
|
|||
greasepencil::convert::lineart_wrap_v3(&legacy_md_lineart, &md_lineart);
|
||||
}
|
||||
|
||||
static void legacy_object_modifier_build(Object &object, GpencilModifierData &legacy_md)
|
||||
{
|
||||
ModifierData &md = legacy_object_modifier_common(
|
||||
object, eModifierType_GreasePencilBuild, legacy_md);
|
||||
auto &md_build = reinterpret_cast<GreasePencilBuildModifierData &>(md);
|
||||
auto &legacy_md_build = reinterpret_cast<BuildGpencilModifierData &>(legacy_md);
|
||||
|
||||
md_build.flag = 0;
|
||||
if (legacy_md_build.flag & GP_BUILD_RESTRICT_TIME) {
|
||||
md_build.flag |= MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME;
|
||||
}
|
||||
if (legacy_md_build.flag & GP_BUILD_USE_FADING) {
|
||||
md_build.flag |= MOD_GREASE_PENCIL_BUILD_USE_FADING;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.mode) {
|
||||
case GP_BUILD_MODE_ADDITIVE:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE;
|
||||
break;
|
||||
case GP_BUILD_MODE_CONCURRENT:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT;
|
||||
break;
|
||||
case GP_BUILD_MODE_SEQUENTIAL:
|
||||
default:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.time_alignment) {
|
||||
default:
|
||||
case GP_BUILD_TIMEALIGN_START:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START;
|
||||
break;
|
||||
case GP_BUILD_TIMEALIGN_END:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.time_mode) {
|
||||
default:
|
||||
case GP_BUILD_TIMEMODE_FRAMES:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES;
|
||||
break;
|
||||
case GP_BUILD_TIMEMODE_PERCENTAGE:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE;
|
||||
break;
|
||||
case GP_BUILD_TIMEMODE_DRAWSPEED:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.transition) {
|
||||
default:
|
||||
case GP_BUILD_TRANSITION_GROW:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW;
|
||||
break;
|
||||
case GP_BUILD_TRANSITION_SHRINK:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_SHRINK;
|
||||
break;
|
||||
case GP_BUILD_TRANSITION_VANISH:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
|
||||
break;
|
||||
}
|
||||
|
||||
md_build.start_frame = legacy_md_build.start_frame;
|
||||
md_build.end_frame = legacy_md_build.end_frame;
|
||||
md_build.start_delay = legacy_md_build.start_delay;
|
||||
md_build.length = legacy_md_build.length;
|
||||
md_build.fade_fac = legacy_md_build.fade_fac;
|
||||
md_build.fade_opacity_strength = legacy_md_build.fade_opacity_strength;
|
||||
md_build.fade_thickness_strength = legacy_md_build.fade_thickness_strength;
|
||||
md_build.percentage_fac = legacy_md_build.percentage_fac;
|
||||
md_build.speed_fac = legacy_md_build.speed_fac;
|
||||
md_build.speed_maxgap = legacy_md_build.speed_maxgap;
|
||||
STRNCPY(md_build.target_vgname, legacy_md_build.target_vgname);
|
||||
|
||||
legacy_object_modifier_influence(md_build.influence,
|
||||
legacy_md_build.layername,
|
||||
legacy_md_build.layer_pass,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_LAYER,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_LAYERPASS,
|
||||
&legacy_md_build.material,
|
||||
legacy_md_build.pass_index,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_MATERIAL,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_PASS,
|
||||
legacy_md_build.target_vgname,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_VGROUP,
|
||||
nullptr,
|
||||
false);
|
||||
}
|
||||
|
||||
static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
|
||||
{
|
||||
BLI_assert(BLI_listbase_is_empty(&object.modifiers));
|
||||
|
@ -1818,6 +1909,8 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
|
|||
legacy_object_modifier_weight_lineart(object, *gpd_md);
|
||||
break;
|
||||
case eGpencilModifierType_Build:
|
||||
legacy_object_modifier_build(object, *gpd_md);
|
||||
break;
|
||||
case eGpencilModifierType_Simplify:
|
||||
case eGpencilModifierType_Texture:
|
||||
break;
|
||||
|
|
|
@ -298,7 +298,7 @@ void BKE_lattice_resize(Lattice *lt, int uNew, int vNew, int wNew, Object *ltOb)
|
|||
calc_lat_fudu(lt->flag, wNew, &fw, &dw);
|
||||
|
||||
/* If old size is different than resolution changed in interface,
|
||||
* try to do clever reinit of points. Pretty simply idea, we just
|
||||
* try to do clever reinitialize of points. Pretty simply idea, we just
|
||||
* deform new verts by old lattice, but scaling them to match old
|
||||
* size first.
|
||||
*/
|
||||
|
|
|
@ -488,11 +488,11 @@ static void libblock_remap_data(
|
|||
Main *bmain, ID *id, eIDRemapType remap_type, IDRemapper &id_remapper, const int remap_flags)
|
||||
{
|
||||
IDRemap id_remap_data = {
|
||||
/*.type=*/remap_type,
|
||||
/*.bmain=*/bmain,
|
||||
/*.id_remapper=*/id_remapper,
|
||||
/*.id_owner=*/nullptr,
|
||||
/*.flag=*/remap_flags,
|
||||
/*type*/ remap_type,
|
||||
/*bmain*/ bmain,
|
||||
/*id_remapper*/ id_remapper,
|
||||
/*id_owner*/ nullptr,
|
||||
/*flag*/ remap_flags,
|
||||
};
|
||||
|
||||
const bool include_ui = (remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0;
|
||||
|
|
|
@ -34,12 +34,14 @@
|
|||
#include "BKE_customdata.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_legacy_convert.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_multires.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
@ -2125,11 +2127,10 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
}
|
||||
group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
|
||||
|
||||
group->tree_interface.add_socket(DATA_("Geometry"),
|
||||
"",
|
||||
"NodeSocketGeometry",
|
||||
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
|
||||
nullptr);
|
||||
group->tree_interface.add_socket(
|
||||
DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
|
||||
group->tree_interface.add_socket(
|
||||
DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
bNodeTreeInterfaceSocket *angle_io_socket = group->tree_interface.add_socket(
|
||||
DATA_("Angle"), "", "NodeSocketFloat", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
auto &angle_data = *static_cast<bNodeSocketValueFloat *>(angle_io_socket->socket_data);
|
||||
|
@ -2144,7 +2145,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
group_input_angle->locx = -420.0f;
|
||||
group_input_angle->locy = -300.0f;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_angle->outputs) {
|
||||
if (!STREQ(socket->identifier, "Socket_1")) {
|
||||
if (!STREQ(socket->identifier, "Socket_2")) {
|
||||
socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
}
|
||||
|
@ -2152,7 +2153,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
group_input_mesh->locx = -60.0f;
|
||||
group_input_mesh->locy = -100.0f;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_mesh->outputs) {
|
||||
if (!STREQ(socket->identifier, "Socket_0")) {
|
||||
if (!STREQ(socket->identifier, "Socket_1")) {
|
||||
socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
}
|
||||
|
@ -2195,7 +2196,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
nodeFindSocket(group_output, SOCK_IN, "Socket_0"));
|
||||
nodeAddLink(group,
|
||||
group_input_angle,
|
||||
nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_1"),
|
||||
nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_2"),
|
||||
less_than_or_equal,
|
||||
nodeFindSocket(less_than_or_equal, SOCK_IN, "B"));
|
||||
nodeAddLink(group,
|
||||
|
@ -2210,7 +2211,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
nodeFindSocket(boolean_and, SOCK_IN, "Boolean_001"));
|
||||
nodeAddLink(group,
|
||||
group_input_mesh,
|
||||
nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_0"),
|
||||
nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_1"),
|
||||
shade_smooth_edge,
|
||||
nodeFindSocket(shade_smooth_edge, SOCK_IN, "Geometry"));
|
||||
nodeAddLink(group,
|
||||
|
@ -2233,27 +2234,115 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
nodeSetSelected(node, false);
|
||||
}
|
||||
|
||||
BKE_ntree_update_main_tree(&bmain, group, nullptr);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
static ModifierData *create_auto_smooth_modifier(Object &object,
|
||||
const FunctionRef<bNodeTree *()> get_node_group,
|
||||
const float angle)
|
||||
static VectorSet<const bNodeSocket *> build_socket_indices(const Span<const bNode *> nodes)
|
||||
{
|
||||
VectorSet<const bNodeSocket *> result;
|
||||
for (const bNode *node : nodes) {
|
||||
LISTBASE_FOREACH (const bNodeSocket *, socket, &node->inputs) {
|
||||
result.add_new(socket);
|
||||
}
|
||||
LISTBASE_FOREACH (const bNodeSocket *, socket, &node->outputs) {
|
||||
result.add_new(socket);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Checks if the node group is the same as the one generated by #create_auto_smooth_modifier. */
|
||||
static bool is_auto_smooth_node_tree(const bNodeTree &group)
|
||||
{
|
||||
if (group.type != NTREE_GEOMETRY) {
|
||||
return false;
|
||||
}
|
||||
const Span<const bNode *> nodes = group.all_nodes();
|
||||
if (nodes.size() != 10) {
|
||||
return false;
|
||||
}
|
||||
if (!group.geometry_node_asset_traits) {
|
||||
return false;
|
||||
}
|
||||
if (group.geometry_node_asset_traits->flag != GEO_NODE_ASSET_MODIFIER) {
|
||||
return false;
|
||||
}
|
||||
const std::array<StringRef, 10> idnames({"NodeGroupOutput",
|
||||
"NodeGroupInput",
|
||||
"NodeGroupInput",
|
||||
"GeometryNodeSetShadeSmooth",
|
||||
"GeometryNodeSetShadeSmooth",
|
||||
"GeometryNodeInputMeshEdgeAngle",
|
||||
"GeometryNodeInputEdgeSmooth",
|
||||
"GeometryNodeInputShadeSmooth",
|
||||
"FunctionNodeBooleanMath",
|
||||
"FunctionNodeCompare"});
|
||||
for (const int i : nodes.index_range()) {
|
||||
if (nodes[i]->idname != idnames[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (nodes[3]->custom1 != int16_t(bke::AttrDomain::Edge)) {
|
||||
return false;
|
||||
}
|
||||
if (static_cast<bNodeSocket *>(nodes[4]->inputs.last)
|
||||
->default_value_typed<bNodeSocketValueBoolean>()
|
||||
->value != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (nodes[4]->custom1 != int16_t(bke::AttrDomain::Face)) {
|
||||
return false;
|
||||
}
|
||||
if (nodes[8]->custom1 != NODE_BOOLEAN_MATH_AND) {
|
||||
return false;
|
||||
}
|
||||
if (static_cast<NodeFunctionCompare *>(nodes[9]->storage)->operation != NODE_COMPARE_LESS_EQUAL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (BLI_listbase_count(&group.links) != 9) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::array<int, 9> link_from_socket_indices({16, 15, 3, 36, 19, 5, 18, 11, 22});
|
||||
const std::array<int, 9> link_to_socket_indices({23, 0, 24, 20, 21, 8, 9, 12, 10});
|
||||
const VectorSet<const bNodeSocket *> socket_indices = build_socket_indices(nodes);
|
||||
int i;
|
||||
LISTBASE_FOREACH_INDEX (const bNodeLink *, link, &group.links, i) {
|
||||
if (socket_indices.index_of(link->fromsock) != link_from_socket_indices[i]) {
|
||||
return false;
|
||||
}
|
||||
if (socket_indices.index_of(link->tosock) != link_to_socket_indices[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ModifierData *create_auto_smooth_modifier(
|
||||
Object &object,
|
||||
const FunctionRef<bNodeTree *(Library *library)> get_node_group,
|
||||
const float angle)
|
||||
{
|
||||
auto *md = reinterpret_cast<NodesModifierData *>(BKE_modifier_new(eModifierType_Nodes));
|
||||
STRNCPY(md->modifier.name, DATA_("Auto Smooth"));
|
||||
BKE_modifier_unique_name(&object.modifiers, &md->modifier);
|
||||
md->node_group = get_node_group();
|
||||
md->node_group = get_node_group(object.id.lib);
|
||||
id_us_plus(&md->node_group->id);
|
||||
|
||||
md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
|
||||
IDProperty *angle_prop = idprop::create("Socket_1", angle).release();
|
||||
IDProperty *angle_prop = idprop::create("Socket_2", angle).release();
|
||||
auto *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(IDP_ui_data_ensure(angle_prop));
|
||||
ui_data->base.rna_subtype = PROP_ANGLE;
|
||||
ui_data->soft_min = 0.0f;
|
||||
ui_data->soft_max = DEG2RADF(180.0f);
|
||||
IDP_AddToGroup(md->settings.properties, angle_prop);
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_1_use_attribute", 0).release());
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_1_attribute_name", "").release());
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_use_attribute", 0).release());
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_attribute_name", "").release());
|
||||
|
||||
BKE_modifiers_persistent_uid_init(object, md->modifier);
|
||||
return &md->modifier;
|
||||
|
@ -2263,14 +2352,43 @@ static ModifierData *create_auto_smooth_modifier(Object &object,
|
|||
|
||||
void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
|
||||
/* Add the node group lazily and share it among all objects in the main database. */
|
||||
bNodeTree *node_group = nullptr;
|
||||
const auto add_node_group = [&]() {
|
||||
node_group = add_auto_smooth_node_tree(bmain);
|
||||
BKE_ntree_update_main_tree(&bmain, node_group, nullptr);
|
||||
return node_group;
|
||||
/* Add the node group lazily and share it among all objects in the same library. */
|
||||
Map<Library *, bNodeTree *> group_by_library;
|
||||
const auto add_node_group = [&](Library *library) {
|
||||
if (bNodeTree **group = group_by_library.lookup_ptr(library)) {
|
||||
/* Node tree has already been found/created for this versioning call. */
|
||||
return *group;
|
||||
}
|
||||
/* Try to find an existing group added by previous versioning to avoid adding duplicates. */
|
||||
LISTBASE_FOREACH (bNodeTree *, existing_group, &bmain.nodetrees) {
|
||||
if (existing_group->id.lib != library) {
|
||||
continue;
|
||||
}
|
||||
if (is_auto_smooth_node_tree(*existing_group)) {
|
||||
group_by_library.add_new(library, existing_group);
|
||||
return existing_group;
|
||||
}
|
||||
}
|
||||
bNodeTree *new_group = add_auto_smooth_node_tree(bmain);
|
||||
/* Remove the default user. The count is tracked manually when assigning to modifiers. */
|
||||
id_us_min(&new_group->id);
|
||||
|
||||
if (new_group->id.lib != library) {
|
||||
/* Move the node group to the requested library so that library data-blocks don't point to
|
||||
* local data-blocks. This requires making sure the name is unique in that library and
|
||||
* changing the name maps to be consistent with the new state. */
|
||||
new_group->id.lib = library;
|
||||
BKE_id_new_name_validate(&bmain, &bmain.nodetrees, &new_group->id, nullptr, false);
|
||||
if (library) {
|
||||
new_group->id.tag |= LIB_TAG_INDIRECT;
|
||||
}
|
||||
}
|
||||
|
||||
group_by_library.add_new(library, new_group);
|
||||
return new_group;
|
||||
};
|
||||
|
||||
LISTBASE_FOREACH (Object *, object, &bmain.objects) {
|
||||
|
@ -2282,12 +2400,12 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
|
|||
if (!(mesh->flag & ME_AUTOSMOOTH_LEGACY)) {
|
||||
continue;
|
||||
}
|
||||
mesh->flag &= ~ME_AUTOSMOOTH_LEGACY;
|
||||
|
||||
/* Auto-smooth disabled sharp edge tagging when the evaluated mesh had custom normals.
|
||||
* When the original mesh has custom normals, that's a good sign the evaluated mesh will
|
||||
* have custom normals as well. */
|
||||
if (CustomData_has_layer(&mesh->corner_data, CD_CUSTOMLOOPNORMAL)) {
|
||||
bool has_custom_normals = CustomData_has_layer(&mesh->corner_data, CD_CUSTOMLOOPNORMAL);
|
||||
if (has_custom_normals) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2295,6 +2413,15 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
|
|||
* edge tags based on the mesh's smoothing angle. To keep the same behavior, a new modifier has
|
||||
* to be added before that modifier when the option is on. */
|
||||
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
|
||||
if (ELEM(md->type, eModifierType_WeightedNormal, eModifierType_NormalEdit)) {
|
||||
has_custom_normals = true;
|
||||
}
|
||||
if (md->type == eModifierType_Bevel) {
|
||||
BevelModifierData *bmd = reinterpret_cast<BevelModifierData *>(md);
|
||||
if (bmd->flags & MOD_BEVEL_HARDEN_NORMALS) {
|
||||
has_custom_normals = true;
|
||||
}
|
||||
}
|
||||
if (md->type == eModifierType_WeightedNormal) {
|
||||
WeightedNormalModifierData *nmd = reinterpret_cast<WeightedNormalModifierData *>(md);
|
||||
if ((nmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0) {
|
||||
|
@ -2307,24 +2434,28 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
|
|||
/* Some modifiers always generate custom normals which disabled sharp edge tagging, making
|
||||
* adding a modifier at the end unnecessary. Conceptually this is similar to checking if the
|
||||
* evaluated mesh had custom normals. */
|
||||
ModifierData *last_md = static_cast<ModifierData *>(object->modifiers.last);
|
||||
if (last_md) {
|
||||
if (ELEM(last_md->type, eModifierType_WeightedNormal, eModifierType_NormalEdit)) {
|
||||
continue;
|
||||
}
|
||||
if (has_custom_normals) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModifierData *last_md = static_cast<ModifierData *>(object->modifiers.last);
|
||||
ModifierData *new_md = create_auto_smooth_modifier(*object, add_node_group, angle);
|
||||
if (last_md && last_md->type == eModifierType_Subsurf) {
|
||||
if (last_md && last_md->type == eModifierType_Subsurf && has_custom_normals &&
|
||||
(reinterpret_cast<SubsurfModifierData *>(last_md)->flags &
|
||||
eSubsurfModifierFlag_UseCustomNormals) != 0)
|
||||
{
|
||||
/* Add the auto smooth node group before the last subdivision surface modifier if possible.
|
||||
* Subdivision surface modifiers have special handling for interpolating face corner normals,
|
||||
* and recalculating them afterwards isn't usually helpful and can be much slower. */
|
||||
* Subdivision surface modifiers have special handling for interpolating custom normals. */
|
||||
BLI_insertlinkbefore(&object->modifiers, object->modifiers.last, new_md);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(&object->modifiers, new_md);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Mesh *, mesh, &bmain.meshes) {
|
||||
mesh->flag &= ~ME_AUTOSMOOTH_LEGACY;
|
||||
}
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
|
|
@ -1457,6 +1457,31 @@ void BKE_nlastrip_set_active(AnimData *adt, NlaStrip *strip)
|
|||
}
|
||||
}
|
||||
|
||||
static NlaStrip *nlastrip_find_by_name(ListBase /* NlaStrip */ *strips, const char *name)
|
||||
{
|
||||
LISTBASE_FOREACH (NlaStrip *, strip, strips) {
|
||||
if (STREQ(strip->name, name)) {
|
||||
return strip;
|
||||
}
|
||||
|
||||
if (strip->type != NLASTRIP_TYPE_META) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NlaStrip *inner_strip = nlastrip_find_by_name(&strip->strips, name);
|
||||
if (inner_strip != nullptr) {
|
||||
return inner_strip;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NlaStrip *BKE_nlastrip_find_by_name(NlaTrack *nlt, const char *name)
|
||||
{
|
||||
return nlastrip_find_by_name(&nlt->strips, name);
|
||||
}
|
||||
|
||||
bool BKE_nlastrip_within_bounds(NlaStrip *strip, float min, float max)
|
||||
{
|
||||
const float stripLen = (strip) ? strip->end - strip->start : 0.0f;
|
||||
|
|
|
@ -2900,7 +2900,7 @@ bNodeLink *nodeAddLink(
|
|||
}
|
||||
|
||||
if (link != nullptr && link->tosock->is_multi_input()) {
|
||||
link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1;
|
||||
link->multi_input_sort_id = node_count_links(ntree, link->tosock) - 1;
|
||||
}
|
||||
|
||||
return link;
|
||||
|
@ -2966,10 +2966,10 @@ static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree,
|
|||
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
||||
/* We only need to adjust those with a greater index, because the others will have the same
|
||||
* index. */
|
||||
if (link->tosock != sock || link->multi_input_socket_index <= deleted_index) {
|
||||
if (link->tosock != sock || link->multi_input_sort_id <= deleted_index) {
|
||||
continue;
|
||||
}
|
||||
link->multi_input_socket_index -= 1;
|
||||
link->multi_input_sort_id -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2995,7 +2995,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
if (fromlink == nullptr) {
|
||||
if (link->tosock->is_multi_input()) {
|
||||
blender::bke::adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link->tosock, link->multi_input_socket_index);
|
||||
ntree, link->tosock, link->multi_input_sort_id);
|
||||
}
|
||||
nodeRemLink(ntree, link);
|
||||
continue;
|
||||
|
@ -3008,7 +3008,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
link_to_compare->tosock == link->tosock)
|
||||
{
|
||||
blender::bke::adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index);
|
||||
ntree, link_to_compare->tosock, link_to_compare->multi_input_sort_id);
|
||||
duplicate_links_to_remove.append_non_duplicates(link_to_compare);
|
||||
}
|
||||
}
|
||||
|
@ -3395,7 +3395,7 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
|
|||
/* Only bother adjusting if the socket is not on the node we're deleting. */
|
||||
if (link->tonode != node && link->tosock->is_multi_input()) {
|
||||
adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link->tosock, link->multi_input_socket_index);
|
||||
ntree, link->tosock, link->multi_input_sort_id);
|
||||
}
|
||||
LISTBASE_FOREACH (const bNodeSocket *, sock, lb) {
|
||||
if (link->fromsock == sock || link->tosock == sock) {
|
||||
|
|
|
@ -135,7 +135,7 @@ static void update_directly_linked_links_and_sockets(const bNodeTree &ntree)
|
|||
std::sort(socket->runtime->directly_linked_links.begin(),
|
||||
socket->runtime->directly_linked_links.end(),
|
||||
[&](const bNodeLink *a, const bNodeLink *b) {
|
||||
return a->multi_input_socket_index > b->multi_input_socket_index;
|
||||
return a->multi_input_sort_id > b->multi_input_sort_id;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1190,6 +1190,10 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(const blender::StringRe
|
|||
const NodeTreeInterfaceSocketFlag flag,
|
||||
bNodeTreeInterfacePanel *parent)
|
||||
{
|
||||
/* Check that each interface socket is either an input or an output. Technically, it can be both
|
||||
* at the same time, but we don't want that for the time being. */
|
||||
BLI_assert(((NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT) & flag) !=
|
||||
(NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT));
|
||||
if (parent == nullptr) {
|
||||
parent = &root_panel;
|
||||
}
|
||||
|
|
|
@ -616,9 +616,9 @@ class NodeTreeMainUpdater {
|
|||
struct InternalLink {
|
||||
bNodeSocket *from;
|
||||
bNodeSocket *to;
|
||||
int multi_input_socket_index = 0;
|
||||
int multi_input_sort_id = 0;
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_3(InternalLink, from, to, multi_input_socket_index);
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_3(InternalLink, from, to, multi_input_sort_id);
|
||||
};
|
||||
|
||||
const bNodeLink *first_non_dangling_link(const bNodeTree &ntree,
|
||||
|
@ -660,7 +660,7 @@ class NodeTreeMainUpdater {
|
|||
const Span<const bNodeLink *> connected_links = input_socket->directly_linked_links();
|
||||
const bNodeLink *connected_link = first_non_dangling_link(ntree, connected_links);
|
||||
|
||||
const int index = connected_link ? connected_link->multi_input_socket_index :
|
||||
const int index = connected_link ? connected_link->multi_input_sort_id :
|
||||
std::max<int>(0, connected_links.size() - 1);
|
||||
expected_internal_links.append(InternalLink{const_cast<bNodeSocket *>(input_socket),
|
||||
const_cast<bNodeSocket *>(output_socket),
|
||||
|
@ -677,8 +677,7 @@ class NodeTreeMainUpdater {
|
|||
node->runtime->internal_links.begin(),
|
||||
node->runtime->internal_links.end(),
|
||||
[&](const bNodeLink &link) {
|
||||
const InternalLink internal_link{
|
||||
link.fromsock, link.tosock, link.multi_input_socket_index};
|
||||
const InternalLink internal_link{link.fromsock, link.tosock, link.multi_input_sort_id};
|
||||
return expected_internal_links.as_span().contains(internal_link);
|
||||
});
|
||||
|
||||
|
@ -736,7 +735,7 @@ class NodeTreeMainUpdater {
|
|||
link.fromsock = internal_link.from;
|
||||
link.tonode = &node;
|
||||
link.tosock = internal_link.to;
|
||||
link.multi_input_socket_index = internal_link.multi_input_socket_index;
|
||||
link.multi_input_sort_id = internal_link.multi_input_sort_id;
|
||||
link.flag |= NODE_LINK_VALID;
|
||||
node.runtime->internal_links.append(link);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "DNA_object_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_subdiv.hh"
|
||||
|
@ -83,7 +84,7 @@ static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene,
|
|||
bool BKE_subsurf_modifier_use_custom_loop_normals(const SubsurfModifierData *smd, const Mesh *mesh)
|
||||
{
|
||||
return smd->flags & eSubsurfModifierFlag_UseCustomNormals &&
|
||||
mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner;
|
||||
CustomData_has_layer(&mesh->corner_data, CD_CUSTOMLOOPNORMAL);
|
||||
}
|
||||
|
||||
static bool is_subdivision_evaluation_possible_on_gpu()
|
||||
|
|
|
@ -190,6 +190,18 @@ inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
|||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &selection,
|
||||
const VArray<T> src,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||
src.materialize_compressed(src_offsets[src_i], dst.slice(dst_offsets[dst_i]));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void gather_to_groups(const OffsetIndices<int> dst_offsets,
|
||||
const IndexMask &src_selection,
|
||||
|
|
|
@ -244,33 +244,69 @@ CPPType::CPPType(TypeTag<T> /*type*/,
|
|||
copy_assign_compressed_ = copy_assign_compressed_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_constructible_v<T>) {
|
||||
copy_construct_ = copy_construct_cb<T>;
|
||||
copy_construct_indices_ = copy_construct_indices_cb<T>;
|
||||
copy_construct_compressed_ = copy_construct_compressed_cb<T>;
|
||||
if constexpr (std::is_trivially_copy_constructible_v<T>) {
|
||||
copy_construct_ = copy_assign_;
|
||||
copy_construct_indices_ = copy_assign_indices_;
|
||||
copy_construct_compressed_ = copy_assign_compressed_;
|
||||
}
|
||||
else {
|
||||
copy_construct_ = copy_construct_cb<T>;
|
||||
copy_construct_indices_ = copy_construct_indices_cb<T>;
|
||||
copy_construct_compressed_ = copy_construct_compressed_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
move_assign_ = move_assign_cb<T>;
|
||||
move_assign_indices_ = move_assign_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_move_assignable_v<T>) {
|
||||
/* This casts away the const from the src pointer. This is fine for trivial types as moving
|
||||
* them does not change the original value. */
|
||||
move_assign_ = reinterpret_cast<decltype(move_assign_)>(copy_assign_);
|
||||
move_assign_indices_ = reinterpret_cast<decltype(move_assign_indices_)>(
|
||||
copy_assign_indices_);
|
||||
}
|
||||
else {
|
||||
move_assign_ = move_assign_cb<T>;
|
||||
move_assign_indices_ = move_assign_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
move_construct_ = move_construct_cb<T>;
|
||||
move_construct_indices_ = move_construct_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_move_constructible_v<T>) {
|
||||
move_construct_ = move_assign_;
|
||||
move_construct_indices_ = move_assign_indices_;
|
||||
}
|
||||
else {
|
||||
move_construct_ = move_construct_cb<T>;
|
||||
move_construct_indices_ = move_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_destructible_v<T>) {
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
relocate_assign_ = relocate_assign_cb<T>;
|
||||
relocate_assign_indices_ = relocate_assign_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_move_assignable_v<T> && std::is_trivially_destructible_v<T>) {
|
||||
relocate_assign_ = move_assign_;
|
||||
relocate_assign_indices_ = move_assign_indices_;
|
||||
|
||||
relocate_construct_ = move_assign_;
|
||||
relocate_construct_indices_ = move_assign_indices_;
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
relocate_construct_ = relocate_construct_cb<T>;
|
||||
relocate_construct_indices_ = relocate_construct_indices_cb<T>;
|
||||
else {
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
relocate_assign_ = relocate_assign_cb<T>;
|
||||
relocate_assign_indices_ = relocate_assign_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
relocate_construct_ = relocate_construct_cb<T>;
|
||||
relocate_construct_indices_ = relocate_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_copy_assignable_v<T>) {
|
||||
fill_assign_indices_ = fill_assign_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_constructible_v<T>) {
|
||||
fill_construct_indices_ = fill_construct_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_constructible_v<T>) {
|
||||
fill_construct_indices_ = fill_assign_indices_;
|
||||
}
|
||||
else {
|
||||
fill_construct_indices_ = fill_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr ((bool)(Flags & CPPTypeFlags::Hashable)) {
|
||||
hash_ = hash_cb<T>;
|
||||
|
|
|
@ -92,8 +92,19 @@ int BLI_rename(const char *from, const char *to) ATTR_NONNULL();
|
|||
*/
|
||||
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL();
|
||||
/**
|
||||
* Deletes the specified file or directory (depending on dir), optionally
|
||||
* doing recursive delete of directory contents.
|
||||
* Deletes the specified file or directory.
|
||||
*
|
||||
* \param dir: Delete an empty directory instead of a file.
|
||||
* The value is ignored when `recursive` is true but should true to make the intention clear.
|
||||
* If the directory is not empty, delete fails.
|
||||
* \param recursive: Recursively delete files including `path` which may be a directory of a file.
|
||||
*
|
||||
* \note Symbolic-Links for (UNIX) behave as follows:
|
||||
* - Never followed, treated as regular files.
|
||||
* - Links are removed, not the files/directories they references.
|
||||
* - When `path` itself links to another directory,
|
||||
* deleting `path` behaves as if a regular file is being deleted.
|
||||
* - If `dir` is true and `path` is a link, delete fails.
|
||||
*
|
||||
* \return zero on success (matching 'remove' behavior).
|
||||
*/
|
||||
|
|
|
@ -128,6 +128,12 @@ class IndexMaskSegment : public OffsetSpan<int64_t, int16_t> {
|
|||
|
||||
IndexMaskSegment slice(const IndexRange &range) const;
|
||||
IndexMaskSegment slice(const int64_t start, const int64_t size) const;
|
||||
|
||||
/**
|
||||
* Get a new segment where each index is modified by the given amount. This works in constant
|
||||
* time, because only the offset value is changed.
|
||||
*/
|
||||
IndexMaskSegment shift(const int64_t shift) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -423,7 +429,7 @@ class IndexMask : private IndexMaskData {
|
|||
/**
|
||||
* Set the bits at indices in the mask to 1 and all other bits to 0.
|
||||
*/
|
||||
void to_bits(MutableBitSpan r_bits) const;
|
||||
void to_bits(MutableBitSpan r_bits, int64_t offset = 0) const;
|
||||
/**
|
||||
* Set the bools at indices in the mask to true and all others to false.
|
||||
*/
|
||||
|
@ -534,6 +540,16 @@ inline void masked_fill(MutableSpan<T> data, const T &value, const IndexMask &ma
|
|||
*/
|
||||
template<typename T> void build_reverse_map(const IndexMask &mask, MutableSpan<T> r_map);
|
||||
|
||||
/**
|
||||
* Joins segments together based on heuristics. Generally, one wants as few segments as possible,
|
||||
* but one also wants full-range-segments if possible and we don't want to copy too many indices
|
||||
* around to reduce the number of segments.
|
||||
*
|
||||
* \return Number of consolidated segments. Those are ordered to the beginning of the span.
|
||||
*/
|
||||
int64_t consolidate_index_mask_segments(MutableSpan<IndexMaskSegment> segments,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #RawMaskIterator Inline Methods
|
||||
* \{ */
|
||||
|
@ -568,6 +584,12 @@ inline IndexMaskSegment IndexMaskSegment::slice(const int64_t start, const int64
|
|||
static_cast<const OffsetSpan<int64_t, int16_t> *>(this)->slice(start, size));
|
||||
}
|
||||
|
||||
inline IndexMaskSegment IndexMaskSegment::shift(const int64_t shift) const
|
||||
{
|
||||
BLI_assert(this->is_empty() || (*this)[0] + shift >= 0);
|
||||
return IndexMaskSegment(this->offset() + shift, this->base_span());
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #IndexMask Inline Methods
|
||||
* \{ */
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
|
||||
namespace blender::index_mask {
|
||||
|
||||
struct AtomicExpr;
|
||||
struct UnionExpr;
|
||||
struct IntersectionExpr;
|
||||
struct DifferenceExpr;
|
||||
|
||||
struct Expr {
|
||||
enum class Type {
|
||||
Atomic,
|
||||
Union,
|
||||
Intersection,
|
||||
Difference,
|
||||
};
|
||||
|
||||
Type type;
|
||||
int index;
|
||||
Vector<const Expr *> terms;
|
||||
|
||||
int expression_array_size() const;
|
||||
|
||||
const AtomicExpr &as_atomic() const;
|
||||
const UnionExpr &as_union() const;
|
||||
const IntersectionExpr &as_intersection() const;
|
||||
const DifferenceExpr &as_difference() const;
|
||||
};
|
||||
|
||||
struct AtomicExpr : public Expr {
|
||||
const IndexMask *mask;
|
||||
};
|
||||
|
||||
struct UnionExpr : public Expr {};
|
||||
|
||||
struct IntersectionExpr : public Expr {};
|
||||
|
||||
struct DifferenceExpr : public Expr {};
|
||||
|
||||
class ExprBuilder {
|
||||
private:
|
||||
ResourceScope scope_;
|
||||
int expr_count_ = 0;
|
||||
|
||||
public:
|
||||
using Term = std::variant<const Expr *, const IndexMask *, IndexRange>;
|
||||
|
||||
const UnionExpr &merge(const Span<Term> terms);
|
||||
const DifferenceExpr &subtract(const Term &main_term, const Span<Term> subtract_terms);
|
||||
const IntersectionExpr &intersect(const Span<Term> terms);
|
||||
|
||||
private:
|
||||
const Expr &term_to_expr(const Term &term);
|
||||
};
|
||||
|
||||
IndexMask evaluate_expression(const Expr &expression, IndexMaskMemory &memory);
|
||||
|
||||
inline int Expr::expression_array_size() const
|
||||
{
|
||||
return this->index + 1;
|
||||
}
|
||||
|
||||
inline const AtomicExpr &Expr::as_atomic() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Atomic);
|
||||
return static_cast<const AtomicExpr &>(*this);
|
||||
}
|
||||
|
||||
inline const UnionExpr &Expr::as_union() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Union);
|
||||
return static_cast<const UnionExpr &>(*this);
|
||||
}
|
||||
|
||||
inline const IntersectionExpr &Expr::as_intersection() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Intersection);
|
||||
return static_cast<const IntersectionExpr &>(*this);
|
||||
}
|
||||
|
||||
inline const DifferenceExpr &Expr::as_difference() const
|
||||
{
|
||||
BLI_assert(this->type == Type::Difference);
|
||||
return static_cast<const DifferenceExpr &>(*this);
|
||||
}
|
||||
|
||||
} // namespace blender::index_mask
|
|
@ -167,6 +167,14 @@ class IndexRange {
|
|||
return size_ == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new index range with the same beginning but a different end.
|
||||
*/
|
||||
constexpr IndexRange with_new_end(const int64_t new_end) const
|
||||
{
|
||||
return IndexRange::from_begin_end(start_, new_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new range starting at the end of the current one.
|
||||
*/
|
||||
|
|
|
@ -213,6 +213,38 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
|
|||
this->provide_buffer(aligned_buffer.ptr(), Size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some algorithms can be implemented more efficiently by over-allocating the destination memory
|
||||
* a bit. This allows the algorithm not to worry about having enough memory. Generally, this can
|
||||
* be a useful strategy if the actual required memory is not known in advance, but an upper bound
|
||||
* can be found. Ideally, one can free the over-allocated memory in the end again to reduce
|
||||
* memory consumption.
|
||||
*
|
||||
* A linear allocator generally does allow freeing any memory. However, there is one exception.
|
||||
* One can free the end of the last allocation (but not any previous allocation). While uses of
|
||||
* this approach are quite limited, it's still the best option in some situations.
|
||||
*/
|
||||
void free_end_of_previous_allocation(const int64_t original_allocation_size,
|
||||
const void *free_after)
|
||||
{
|
||||
/* If the original allocation size was large, it might have been separately allocated. In this
|
||||
* case, we can't free the end of it anymore. */
|
||||
if (original_allocation_size <= large_buffer_threshold) {
|
||||
const int64_t new_begin = uintptr_t(free_after);
|
||||
BLI_assert(new_begin <= current_begin_);
|
||||
#ifndef NDEBUG
|
||||
/* This condition is not really necessary but it helps finding the cases where memory was
|
||||
* freed. */
|
||||
const int64_t freed_bytes_num = current_begin_ - new_begin;
|
||||
if (freed_bytes_num > 0) {
|
||||
current_begin_ = new_begin;
|
||||
}
|
||||
#else
|
||||
current_begin_ = new_begin;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This allocator takes ownership of the buffers owned by `other`. Therefor, when `other` is
|
||||
* destructed, memory allocated using it is not freed.
|
||||
|
|
|
@ -65,6 +65,11 @@ template<typename T> inline T clamp(const T &a, const T &min, const T &max)
|
|||
return std::clamp(a, min, max);
|
||||
}
|
||||
|
||||
template<typename T> inline T step(const T &edge, const T &value)
|
||||
{
|
||||
return value < edge ? 0 : 1;
|
||||
}
|
||||
|
||||
template<typename T> inline T mod(const T &a, const T &b)
|
||||
{
|
||||
return std::fmod(a, b);
|
||||
|
|
|
@ -126,6 +126,27 @@ template<typename T, int Size>
|
|||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline VecBase<T, Size> step(const VecBase<T, Size> &edge,
|
||||
const VecBase<T, Size> &value)
|
||||
{
|
||||
VecBase<T, Size> result = value;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = math::step(edge[i], result[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline VecBase<T, Size> step(const T &edge, const VecBase<T, Size> &value)
|
||||
{
|
||||
VecBase<T, Size> result = value;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = math::step(edge, result[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline VecBase<T, Size> mod(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
|
||||
{
|
||||
|
|
|
@ -73,6 +73,7 @@ void parallel_for_weighted_impl(IndexRange range,
|
|||
int64_t grain_size,
|
||||
FunctionRef<void(IndexRange)> function,
|
||||
FunctionRef<void(IndexRange, MutableSpan<int64_t>)> task_sizes_fn);
|
||||
void memory_bandwidth_bound_task_impl(FunctionRef<void()> function);
|
||||
} // namespace detail
|
||||
|
||||
template<typename Function>
|
||||
|
@ -247,4 +248,26 @@ template<typename Function> inline void isolate_task(const Function &function)
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Should surround parallel code that is highly bandwidth intensive, e.g. it just fills a buffer
|
||||
* with no or just few additional operations. If the buffers are large, it's beneficial to limit
|
||||
* the number of threads doing the work because that just creates more overhead on the hardware
|
||||
* level and doesn't provide a notable performance benefit beyond a certain point.
|
||||
*/
|
||||
template<typename Function>
|
||||
inline void memory_bandwidth_bound_task(const int64_t approximate_bytes_touched,
|
||||
const Function &function)
|
||||
{
|
||||
/* Don't limit threading when all touched memory can stay in the CPU cache, because there a much
|
||||
* higher memory bandwidth is available compared to accessing RAM. This value is supposed to be
|
||||
* on the order of the L3 cache size. Accessing that value is not quite straight forward and even
|
||||
* if it was, it's not clear if using the exact cache size would be beneficial because there is
|
||||
* often more stuff going on on the CPU at the same time. */
|
||||
if (approximate_bytes_touched <= 8 * 1024 * 1024) {
|
||||
function();
|
||||
return;
|
||||
}
|
||||
detail::memory_bandwidth_bound_task_impl(function);
|
||||
}
|
||||
|
||||
} // namespace blender::threading
|
||||
|
|
|
@ -82,6 +82,7 @@ set(SRC
|
|||
intern/hash_tables.cc
|
||||
intern/implicit_sharing.cc
|
||||
intern/index_mask.cc
|
||||
intern/index_mask_expression.cc
|
||||
intern/index_range.cc
|
||||
intern/jitter_2d.c
|
||||
intern/kdtree_1d.c
|
||||
|
@ -252,6 +253,7 @@ set(SRC
|
|||
BLI_implicit_sharing_ptr.hh
|
||||
BLI_index_mask.hh
|
||||
BLI_index_mask_fwd.hh
|
||||
BLI_index_mask_expression.hh
|
||||
BLI_index_range.hh
|
||||
BLI_inplace_priority_queue.hh
|
||||
BLI_iterator.h
|
||||
|
@ -516,6 +518,7 @@ if(WITH_GTESTS)
|
|||
tests/BLI_heap_test.cc
|
||||
tests/BLI_implicit_sharing_test.cc
|
||||
tests/BLI_index_mask_test.cc
|
||||
tests/BLI_index_mask_expression_test.cc
|
||||
tests/BLI_index_range_test.cc
|
||||
tests/BLI_inplace_priority_queue_test.cc
|
||||
tests/BLI_kdopbvh_test.cc
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_sys_types.h" /* for intptr_t support */
|
||||
#include "BLI_sys_types.h" /* For `intptr_t` support. */
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
/** Sizes above this must be allocated. */
|
||||
|
@ -292,19 +292,19 @@ bool BLI_file_is_writable(const char *filepath)
|
|||
{
|
||||
bool writable;
|
||||
if (BLI_access(filepath, W_OK) == 0) {
|
||||
/* file exists and I can write to it */
|
||||
/* File exists and I can write to it. */
|
||||
writable = true;
|
||||
}
|
||||
else if (errno != ENOENT) {
|
||||
/* most likely file or containing directory cannot be accessed */
|
||||
/* Most likely file or containing directory cannot be accessed. */
|
||||
writable = false;
|
||||
}
|
||||
else {
|
||||
/* file doesn't exist -- check I can create it in parent directory */
|
||||
/* File doesn't exist -- check I can create it in parent directory. */
|
||||
char parent[FILE_MAX];
|
||||
BLI_path_split_dir_part(filepath, parent, sizeof(parent));
|
||||
#ifdef WIN32
|
||||
/* windows does not have X_OK */
|
||||
/* Windows does not have X_OK. */
|
||||
writable = BLI_access(parent, W_OK) == 0;
|
||||
#else
|
||||
writable = BLI_access(parent, X_OK | W_OK) == 0;
|
||||
|
@ -321,7 +321,7 @@ bool BLI_file_touch(const char *filepath)
|
|||
int c = getc(f);
|
||||
|
||||
if (c == EOF) {
|
||||
/* Empty file, reopen in truncate write mode... */
|
||||
/* Empty file, reopen in truncate write mode. */
|
||||
fclose(f);
|
||||
f = BLI_fopen(filepath, "w+b");
|
||||
}
|
||||
|
@ -734,6 +734,9 @@ int BLI_delete(const char *path, bool dir, bool recursive)
|
|||
|
||||
BLI_assert(!BLI_path_is_rel(path));
|
||||
|
||||
/* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
|
||||
BLI_assert(!(dir == false && recursive == true));
|
||||
|
||||
if (recursive) {
|
||||
err = delete_recursive(path);
|
||||
}
|
||||
|
@ -1089,6 +1092,9 @@ static int recursive_operation_impl(StrBuf *src_buf,
|
|||
* prefixing it with path_dst, recursively scanning subdirectories, and invoking the specified
|
||||
* callbacks for files and subdirectories found as appropriate.
|
||||
*
|
||||
* \note Symbolic links are *not* followed, even when `path_src` links to a directory,
|
||||
* it wont be recursed down. Support for this could be added.
|
||||
*
|
||||
* \param path_src: Top-level source path.
|
||||
* \param path_dst: Top-level destination path.
|
||||
* \param callback_dir_pre: Optional, to be invoked before entering a subdirectory,
|
||||
|
@ -1222,7 +1228,7 @@ static int delete_soft(const char *file, const char **error_message)
|
|||
int pid = fork();
|
||||
|
||||
if (pid != 0) {
|
||||
/* Parent process */
|
||||
/* Parent process. */
|
||||
int wstatus = 0;
|
||||
|
||||
waitpid(pid, &wstatus, 0);
|
||||
|
@ -1243,7 +1249,7 @@ static int delete_soft(const char *file, const char **error_message)
|
|||
execvp(args[0], (char **)args);
|
||||
|
||||
*error_message = "Forking process failed.";
|
||||
return -1; /* This should only be reached if execvp fails and stack isn't replaced. */
|
||||
return -1; /* This should only be reached if `execvp` fails and stack isn't replaced. */
|
||||
}
|
||||
# endif
|
||||
|
||||
|
@ -1278,6 +1284,8 @@ int BLI_access(const char *filepath, int mode)
|
|||
int BLI_delete(const char *path, bool dir, bool recursive)
|
||||
{
|
||||
BLI_assert(!BLI_path_is_rel(path));
|
||||
/* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
|
||||
BLI_assert(!(dir == false && recursive == true));
|
||||
|
||||
if (recursive) {
|
||||
return recursive_operation(path, nullptr, nullptr, delete_single_file, delete_callback_post);
|
||||
|
@ -1347,13 +1355,13 @@ static int copy_callback_pre(const char *from, const char *to)
|
|||
return RecursiveOp_Callback_Error;
|
||||
}
|
||||
|
||||
/* create a directory */
|
||||
/* Create a directory. */
|
||||
if (mkdir(to, st.st_mode)) {
|
||||
perror("mkdir");
|
||||
return RecursiveOp_Callback_Error;
|
||||
}
|
||||
|
||||
/* set proper owner and group on new directory */
|
||||
/* Set proper owner and group on new directory. */
|
||||
if (chown(to, st.st_uid, st.st_gid)) {
|
||||
perror("chown");
|
||||
return RecursiveOp_Callback_Error;
|
||||
|
@ -1380,12 +1388,12 @@ static int copy_single_file(const char *from, const char *to)
|
|||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
/* symbolic links should be copied in special way */
|
||||
/* Symbolic links should be copied in special way. */
|
||||
char *link_buffer;
|
||||
int need_free;
|
||||
int64_t link_len;
|
||||
|
||||
/* get large enough buffer to read link content */
|
||||
/* Get large enough buffer to read link content. */
|
||||
if ((st.st_size + 1) < sizeof(buf)) {
|
||||
link_buffer = buf;
|
||||
need_free = 0;
|
||||
|
@ -1423,7 +1431,7 @@ static int copy_single_file(const char *from, const char *to)
|
|||
return RecursiveOp_Callback_OK;
|
||||
}
|
||||
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
|
||||
/* copy special type of file */
|
||||
/* Copy special type of file. */
|
||||
if (mknod(to, st.st_mode, st.st_rdev)) {
|
||||
perror("mknod");
|
||||
return RecursiveOp_Callback_Error;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "BLI_bit_vector.hh"
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_index_mask_expression.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_sort.hh"
|
||||
|
@ -211,15 +212,11 @@ IndexMask IndexMask::shift(const int64_t offset, IndexMaskMemory &memory) const
|
|||
return shifted_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges consecutive segments in some cases. Having fewer but larger segments generally allows for
|
||||
* better performance when using the mask later on.
|
||||
*/
|
||||
static void consolidate_segments(Vector<IndexMaskSegment, 16> &segments,
|
||||
IndexMaskMemory & /*memory*/)
|
||||
int64_t consolidate_index_mask_segments(MutableSpan<IndexMaskSegment> segments,
|
||||
IndexMaskMemory & /*memory*/)
|
||||
{
|
||||
if (segments.is_empty()) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Span<int16_t> static_indices = get_static_indices_array();
|
||||
|
@ -268,7 +265,13 @@ static void consolidate_segments(Vector<IndexMaskSegment, 16> &segments,
|
|||
finish_group(segments.size() - 1);
|
||||
|
||||
/* Remove all segments that have been merged into previous segments. */
|
||||
segments.remove_if([](const IndexMaskSegment segment) { return segment.is_empty(); });
|
||||
const int64_t new_segments_num = std::remove_if(segments.begin(),
|
||||
segments.end(),
|
||||
[](const IndexMaskSegment segment) {
|
||||
return segment.is_empty();
|
||||
}) -
|
||||
segments.begin();
|
||||
return new_segments_num;
|
||||
}
|
||||
|
||||
IndexMask IndexMask::from_segments(const Span<IndexMaskSegment> segments, IndexMaskMemory &memory)
|
||||
|
@ -389,162 +392,12 @@ struct ParallelSegmentsCollector {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a range to potentially multiple index mask segments.
|
||||
*/
|
||||
static void range_to_segments(const IndexRange range, Vector<IndexMaskSegment, 16> &r_segments)
|
||||
{
|
||||
const Span<int16_t> static_indices = get_static_indices_array();
|
||||
for (int64_t start = 0; start < range.size(); start += max_segment_size) {
|
||||
const int64_t size = std::min(max_segment_size, range.size() - start);
|
||||
r_segments.append_as(range.start() + start, static_indices.take_front(size));
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t get_size_before_gap(const Span<int16_t> indices)
|
||||
{
|
||||
BLI_assert(indices.size() >= 2);
|
||||
if (indices[1] > indices[0] + 1) {
|
||||
/* For sparse indices, often the next gap is just after the next index.
|
||||
* In this case we can skip the logarithmic check below. */
|
||||
return 1;
|
||||
}
|
||||
return unique_sorted_indices::find_size_of_next_range(indices);
|
||||
}
|
||||
|
||||
static void inverted_indices_to_segments(const IndexMaskSegment segment,
|
||||
LinearAllocator<> &allocator,
|
||||
Vector<IndexMaskSegment, 16> &r_segments)
|
||||
{
|
||||
constexpr int64_t range_threshold = 64;
|
||||
const int64_t offset = segment.offset();
|
||||
const Span<int16_t> static_indices = get_static_indices_array();
|
||||
|
||||
int64_t inverted_index_count = 0;
|
||||
std::array<int16_t, max_segment_size> inverted_indices_array;
|
||||
auto add_indices = [&](const int16_t start, const int16_t num) {
|
||||
int16_t *new_indices_begin = inverted_indices_array.data() + inverted_index_count;
|
||||
std::iota(new_indices_begin, new_indices_begin + num, start);
|
||||
inverted_index_count += num;
|
||||
};
|
||||
|
||||
auto finish_indices = [&]() {
|
||||
if (inverted_index_count == 0) {
|
||||
return;
|
||||
}
|
||||
MutableSpan<int16_t> offset_indices = allocator.allocate_array<int16_t>(inverted_index_count);
|
||||
offset_indices.copy_from(Span(inverted_indices_array).take_front(inverted_index_count));
|
||||
r_segments.append_as(offset, offset_indices);
|
||||
inverted_index_count = 0;
|
||||
};
|
||||
|
||||
Span<int16_t> indices = segment.base_span();
|
||||
while (indices.size() > 1) {
|
||||
const int64_t size_before_gap = get_size_before_gap(indices);
|
||||
if (size_before_gap == indices.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const int16_t gap_first = indices[size_before_gap - 1] + 1;
|
||||
const int16_t next = indices[size_before_gap];
|
||||
const int16_t gap_size = next - gap_first;
|
||||
if (gap_size > range_threshold) {
|
||||
finish_indices();
|
||||
r_segments.append_as(offset + gap_first, static_indices.take_front(gap_size));
|
||||
}
|
||||
else {
|
||||
add_indices(gap_first, gap_size);
|
||||
}
|
||||
|
||||
indices = indices.drop_front(size_before_gap);
|
||||
}
|
||||
|
||||
finish_indices();
|
||||
}
|
||||
|
||||
static void invert_segments(const IndexMask &mask,
|
||||
const IndexRange segment_range,
|
||||
LinearAllocator<> &allocator,
|
||||
Vector<IndexMaskSegment, 16> &r_segments)
|
||||
{
|
||||
for (const int64_t segment_i : segment_range) {
|
||||
const IndexMaskSegment segment = mask.segment(segment_i);
|
||||
inverted_indices_to_segments(segment, allocator, r_segments);
|
||||
|
||||
const IndexMaskSegment next_segment = mask.segment(segment_i + 1);
|
||||
const int64_t between_start = segment.last() + 1;
|
||||
const int64_t size_between_segments = next_segment[0] - segment.last() - 1;
|
||||
const IndexRange range_between_segments(between_start, size_between_segments);
|
||||
if (!range_between_segments.is_empty()) {
|
||||
range_to_segments(range_between_segments, r_segments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IndexMask IndexMask::complement(const IndexRange universe, IndexMaskMemory &memory) const
|
||||
{
|
||||
if (this->is_empty()) {
|
||||
return universe;
|
||||
}
|
||||
if (universe.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
const std::optional<IndexRange> this_range = this->to_range();
|
||||
if (this_range) {
|
||||
const bool first_in_range = this_range->first() <= universe.first();
|
||||
const bool last_in_range = this_range->last() >= universe.last();
|
||||
if (first_in_range && last_in_range) {
|
||||
/* This mask fills the entire universe, so the complement is empty. */
|
||||
return {};
|
||||
}
|
||||
if (first_in_range) {
|
||||
/* This mask is a range that contains the start of the universe.
|
||||
* The complement is a range that contains the end of the universe. */
|
||||
return IndexRange::from_begin_end(this_range->one_after_last(), universe.one_after_last());
|
||||
}
|
||||
if (last_in_range) {
|
||||
/* This mask is a range that contains the end of the universe.
|
||||
* The complement is a range that contains the start of the universe. */
|
||||
return IndexRange::from_begin_end(universe.first(), this_range->first());
|
||||
}
|
||||
}
|
||||
|
||||
Vector<IndexMaskSegment, 16> segments;
|
||||
|
||||
if (universe.start() < this->first()) {
|
||||
range_to_segments(universe.take_front(this->first() - universe.start()), segments);
|
||||
}
|
||||
|
||||
if (!this_range) {
|
||||
const int64_t segments_num = this->segments_num();
|
||||
|
||||
constexpr int64_t min_grain_size = 16;
|
||||
constexpr int64_t max_grain_size = 4096;
|
||||
const int64_t threads_num = BLI_system_thread_count();
|
||||
const int64_t grain_size = std::clamp(
|
||||
segments_num / threads_num, min_grain_size, max_grain_size);
|
||||
|
||||
const IndexRange non_last_segments = IndexRange(segments_num).drop_back(1);
|
||||
if (segments_num < min_grain_size) {
|
||||
invert_segments(*this, non_last_segments, memory, segments);
|
||||
}
|
||||
else {
|
||||
ParallelSegmentsCollector segments_collector;
|
||||
threading::parallel_for(non_last_segments, grain_size, [&](const IndexRange range) {
|
||||
ParallelSegmentsCollector::LocalData &local_data =
|
||||
segments_collector.data_by_thread.local();
|
||||
invert_segments(*this, range, local_data.allocator, local_data.segments);
|
||||
});
|
||||
segments_collector.reduce(memory, segments);
|
||||
}
|
||||
inverted_indices_to_segments(this->segment(segments_num - 1), memory, segments);
|
||||
}
|
||||
|
||||
if (universe.last() > this->first()) {
|
||||
range_to_segments(universe.take_back(universe.last() - this->last()), segments);
|
||||
}
|
||||
|
||||
return IndexMask::from_segments(segments, memory);
|
||||
ExprBuilder builder;
|
||||
const IndexMask universe_mask{universe};
|
||||
const Expr &expr = builder.subtract(&universe_mask, {this});
|
||||
return evaluate_expression(expr, memory);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -580,7 +433,8 @@ IndexMask IndexMask::from_indices(const Span<T> indices, IndexMaskMemory &memory
|
|||
});
|
||||
segments_collector.reduce(memory, segments);
|
||||
}
|
||||
consolidate_segments(segments, memory);
|
||||
const int64_t consolidated_segments_num = consolidate_index_mask_segments(segments, memory);
|
||||
segments.resize(consolidated_segments_num);
|
||||
return IndexMask::from_segments(segments, memory);
|
||||
}
|
||||
|
||||
|
@ -636,13 +490,9 @@ IndexMask IndexMask::from_union(const IndexMask &mask_a,
|
|||
const IndexMask &mask_b,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
const int64_t new_size = math::max(mask_a.min_array_size(), mask_b.min_array_size());
|
||||
Array<bool> tmp(new_size, false);
|
||||
mask_a.foreach_index_optimized<int64_t>(GrainSize(2048),
|
||||
[&](const int64_t i) { tmp[i] = true; });
|
||||
mask_b.foreach_index_optimized<int64_t>(GrainSize(2048),
|
||||
[&](const int64_t i) { tmp[i] = true; });
|
||||
return IndexMask::from_bools(tmp, memory);
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_a, &mask_b});
|
||||
return evaluate_expression(expr, memory);
|
||||
}
|
||||
|
||||
IndexMask IndexMask::from_initializers(const Span<Initializer> initializers,
|
||||
|
@ -684,17 +534,20 @@ template<typename T> void IndexMask::to_indices(MutableSpan<T> r_indices) const
|
|||
});
|
||||
}
|
||||
|
||||
void IndexMask::to_bits(MutableBitSpan r_bits) const
|
||||
void IndexMask::to_bits(MutableBitSpan r_bits, const int64_t offset) const
|
||||
{
|
||||
BLI_assert(r_bits.size() >= this->min_array_size());
|
||||
BLI_assert(r_bits.size() >= this->min_array_size() + offset);
|
||||
r_bits.reset_all();
|
||||
this->foreach_segment_optimized([&](const auto segment) {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(segment)>, IndexRange>) {
|
||||
const IndexRange range = segment;
|
||||
r_bits.slice(range).set_all();
|
||||
const IndexRange shifted_range = range.shift(offset);
|
||||
r_bits.slice(shifted_range).set_all();
|
||||
}
|
||||
else {
|
||||
for (const int64_t i : segment) {
|
||||
const IndexMaskSegment indices = segment;
|
||||
const IndexMaskSegment shifted_indices = indices.shift(offset);
|
||||
for (const int64_t i : shifted_indices) {
|
||||
r_bits[i].set();
|
||||
}
|
||||
}
|
||||
|
@ -785,7 +638,8 @@ IndexMask from_predicate_impl(
|
|||
segments_collector.reduce(memory, segments);
|
||||
}
|
||||
|
||||
consolidate_segments(segments, memory);
|
||||
const int64_t consolidated_segments_num = consolidate_index_mask_segments(segments, memory);
|
||||
segments.resize(consolidated_segments_num);
|
||||
return IndexMask::from_segments(segments, memory);
|
||||
}
|
||||
} // namespace detail
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4800,8 +4800,9 @@ void lookat_m4(
|
|||
|
||||
i_multmatrix(mat1, mat);
|
||||
|
||||
mat1[1][1] = mat1[2][2] = 1.0f; /* be careful here to reinit */
|
||||
mat1[1][2] = mat1[2][1] = 0.0f; /* those modified by the last */
|
||||
/* Be careful here to reinitialize those modified by the last. */
|
||||
mat1[1][1] = mat1[2][2] = 1.0f;
|
||||
mat1[1][2] = mat1[2][1] = 0.0f;
|
||||
|
||||
/* paragraph */
|
||||
if (hyp != 0.0f) { /* rotate Y */
|
||||
|
|
|
@ -24,10 +24,12 @@ OffsetIndices<int> accumulate_counts_to_offsets(MutableSpan<int> counts_to_offse
|
|||
|
||||
void fill_constant_group_size(const int size, const int start_offset, MutableSpan<int> offsets)
|
||||
{
|
||||
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
offsets[i] = size * i + start_offset;
|
||||
}
|
||||
threading::memory_bandwidth_bound_task(offsets.size_in_bytes(), [&]() {
|
||||
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
offsets[i] = size * i + start_offset;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -52,11 +54,14 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
|
|||
const Span<int> indices,
|
||||
MutableSpan<int> sizes)
|
||||
{
|
||||
threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sizes[i] = offsets[indices[i]].size();
|
||||
}
|
||||
});
|
||||
threading::memory_bandwidth_bound_task(
|
||||
sizes.size_in_bytes() + offsets.data().size_in_bytes() + indices.size_in_bytes(), [&]() {
|
||||
threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sizes[i] = offsets[indices[i]].size();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
|
||||
|
|
|
@ -223,4 +223,32 @@ void parallel_for_weighted_impl(
|
|||
});
|
||||
}
|
||||
|
||||
void memory_bandwidth_bound_task_impl(const FunctionRef<void()> function)
|
||||
{
|
||||
#ifdef WITH_TBB
|
||||
/* This is the maximum number of threads that may perform these memory bandwidth bound tasks at
|
||||
* the same time. Often fewer threads are already enough to use up the full bandwidth capacity.
|
||||
* Additional threads usually have a negligible benefit and can even make performance worse.
|
||||
*
|
||||
* It's better to use fewer threads here so that the CPU cores can do other tasks at the same
|
||||
* time which may be more compute intensive. */
|
||||
const int num_threads = 8;
|
||||
if (num_threads >= BLI_task_scheduler_num_threads()) {
|
||||
/* Avoid overhead of using a task arena when it would not have any effect anyway. */
|
||||
function();
|
||||
return;
|
||||
}
|
||||
static tbb::task_arena arena{num_threads};
|
||||
|
||||
/* Make sure the lazy threading hints are send now, because they shouldn't be send out of an
|
||||
* isolated region. */
|
||||
lazy_threading::send_hint();
|
||||
lazy_threading::ReceiverIsolation isolation;
|
||||
|
||||
arena.execute(function);
|
||||
#else
|
||||
function();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace blender::threading::detail
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_index_mask_expression.hh"
|
||||
#include "BLI_rand.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_strict_flags.h"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::index_mask::tests {
|
||||
|
||||
TEST(index_mask_expression, Union)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({5, IndexRange(50, 100), 100'000}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers({IndexRange(10, 10), 60, 200}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_a, &mask_b});
|
||||
const IndexMask union_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(union_mask,
|
||||
IndexMask::from_initializers(
|
||||
{5, IndexRange(10, 10), IndexRange(50, 100), 200, 100'000}, memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, UnionMulti)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({3, 5, 6, 8, 9}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers({4, 6, 7, 12}, memory);
|
||||
const IndexMask mask_c = IndexMask::from_initializers({0, 5}, memory);
|
||||
const IndexMask mask_d = IndexMask::from_initializers({6, 7, 10}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_a, &mask_b, &mask_c, &mask_d});
|
||||
const IndexMask union_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(union_mask, IndexMask::from_initializers({0, 3, 4, 5, 6, 7, 8, 9, 10, 12}, memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, IntersectMulti)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({3, 5, 6, 8, 9}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers({2, 5, 6, 10}, memory);
|
||||
const IndexMask mask_c = IndexMask::from_initializers({4, 5, 6}, memory);
|
||||
const IndexMask mask_d = IndexMask::from_initializers({1, 5, 10}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.intersect({&mask_a, &mask_b, &mask_c, &mask_d});
|
||||
const IndexMask intersect_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(intersect_mask, IndexMask::from_initializers({5}, memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, DifferenceMulti)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({1, 2, 3, 5, 6, 7, 9, 10}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers({2, 5, 6, 10}, memory);
|
||||
const IndexMask mask_c = IndexMask::from_initializers({4, 5, 6}, memory);
|
||||
const IndexMask mask_d = IndexMask::from_initializers({1, 5, 10}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.subtract(&mask_a, {&mask_b, &mask_c, &mask_d});
|
||||
const IndexMask difference_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(difference_mask, IndexMask::from_initializers({3, 7, 9}, memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, Intersection)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({5, IndexRange(50, 100), 100'000}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers(
|
||||
{5, 6, IndexRange(100, 100), 80000, 100'000}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.intersect({&mask_a, &mask_b});
|
||||
const IndexMask intersection_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(intersection_mask,
|
||||
IndexMask::from_initializers({5, IndexRange(100, 50), 100'000}, memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, Difference)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({5, IndexRange(50, 100), 100'000}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers({5, 60, IndexRange(100, 20)}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.subtract(&mask_a, {&mask_b});
|
||||
const IndexMask difference_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(difference_mask,
|
||||
IndexMask::from_initializers(
|
||||
{IndexRange(50, 10), IndexRange(61, 39), IndexRange(120, 30), 100'000}, memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, FizzBuzz)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_3 = IndexMask::from_every_nth(3, 11, 0, memory); /* 0 - 30 */
|
||||
const IndexMask mask_5 = IndexMask::from_every_nth(5, 11, 0, memory); /* 0 - 50 */
|
||||
|
||||
{
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_3, &mask_5});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
EXPECT_EQ(
|
||||
result,
|
||||
IndexMask::from_initializers(
|
||||
{0, 3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 35, 40, 45, 50}, memory));
|
||||
}
|
||||
{
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.intersect({&mask_3, &mask_5});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
EXPECT_EQ(result, IndexMask::from_initializers({0, 15, 30}, memory));
|
||||
}
|
||||
{
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.subtract(&mask_3, {&mask_5});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
EXPECT_EQ(result, IndexMask::from_initializers({3, 6, 9, 12, 18, 21, 24, 27}, memory));
|
||||
}
|
||||
{
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge(
|
||||
{&builder.intersect({&mask_3, &mask_5}), &builder.subtract(&mask_3, {&mask_5})});
|
||||
const IndexMask &result = evaluate_expression(expr, memory);
|
||||
EXPECT_EQ(result,
|
||||
IndexMask::from_initializers({0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30}, memory));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, UnionToFullRange)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_1 = IndexMask::from_initializers({2, 4, 5, 7}, memory);
|
||||
const IndexMask mask_2 = IndexMask::from_initializers({6, 8}, memory);
|
||||
const IndexMask mask_3 = IndexMask::from_initializers({1, 3}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_1, &mask_2, &mask_3});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
EXPECT_TRUE(result.to_range().has_value());
|
||||
EXPECT_EQ(*result.to_range(), IndexRange::from_begin_end_inclusive(1, 8));
|
||||
EXPECT_EQ(result.segments_num(), 1);
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, UnionIndividualIndices)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_1 = IndexMask::from_initializers({3}, memory);
|
||||
const IndexMask mask_2 = IndexMask::from_initializers({6}, memory);
|
||||
const IndexMask mask_3 = IndexMask::from_initializers({5}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_1, &mask_2, &mask_3});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
EXPECT_EQ(result, IndexMask::from_initializers({3, 5, 6}, memory));
|
||||
EXPECT_EQ(result.segments_num(), 1);
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, UnionLargeRanges)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a(IndexRange(0, 1'000'000));
|
||||
const IndexMask mask_b(IndexRange(900'000, 1'100'000));
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask_a, &mask_b});
|
||||
const IndexMask result_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(result_mask, IndexMask(IndexRange(0, 2'000'000)));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, SubtractSmall)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask_a = IndexMask::from_initializers({3, 4, 5, 6, 7, 8, 9}, memory);
|
||||
const IndexMask mask_b = IndexMask::from_initializers({5, 7}, memory);
|
||||
const IndexMask mask_c = IndexMask::from_initializers({8}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.subtract(&mask_a, {&mask_b, &mask_c});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(result, IndexMask::from_initializers({3, 4, 6, 9}, memory));
|
||||
EXPECT_EQ(result.segments_num(), 1);
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, RangeTerms)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
ExprBuilder builder;
|
||||
|
||||
const IndexRange range_a = IndexRange::from_begin_end(30'000, 50'000);
|
||||
const IndexRange range_b = IndexRange::from_begin_end(40'000, 100'000);
|
||||
const IndexRange range_c = IndexRange::from_begin_end(45'000, 48'000);
|
||||
|
||||
const Expr &expr = builder.subtract(&builder.merge({range_a, range_b}), {range_c});
|
||||
const IndexMask result_mask = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(result_mask,
|
||||
IndexMask::from_initializers({IndexRange::from_begin_end(30'000, 45'000),
|
||||
IndexRange::from_begin_end(48'000, 100'000)},
|
||||
memory));
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, SingleMask)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask::from_initializers({5, 6, 8, 9}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&mask});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_EQ(result, mask);
|
||||
}
|
||||
|
||||
TEST(index_mask_expression, SubtractSelf)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask ::from_initializers({6, 8, 10, 100}, memory);
|
||||
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.subtract(&mask, {&mask});
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
|
||||
EXPECT_TRUE(result.is_empty());
|
||||
}
|
||||
|
||||
/* Disable benchmark by default. */
|
||||
#if 0
|
||||
TEST(index_mask_expression, Benchmark)
|
||||
{
|
||||
# ifdef NDEBUG
|
||||
const int64_t iterations = 100;
|
||||
# else
|
||||
const int64_t iterations = 1;
|
||||
# endif
|
||||
|
||||
for ([[maybe_unused]] const int64_t _1 : IndexRange(5)) {
|
||||
IndexMaskMemory m;
|
||||
const IndexMask a = IndexMask::from_every_nth(3, 1'000'000, 0, m);
|
||||
const IndexMask b = IndexMask::from_every_nth(100, 5'000, 0, m);
|
||||
ExprBuilder builder;
|
||||
const Expr &expr = builder.merge({&a, &b});
|
||||
|
||||
SCOPED_TIMER("benchmark");
|
||||
for ([[maybe_unused]] const int64_t _2 : IndexRange(iterations)) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask result = evaluate_expression(expr, memory);
|
||||
UNUSED_VARS(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace blender::index_mask::tests
|
|
@ -173,6 +173,21 @@ TEST(math_base, ClampInt)
|
|||
EXPECT_EQ(math::clamp(111, -50, 101), 101);
|
||||
}
|
||||
|
||||
TEST(math_base, StepLessThan)
|
||||
{
|
||||
EXPECT_EQ(math::step(0.5f, 0.3f), 0.0f);
|
||||
}
|
||||
|
||||
TEST(math_base, StepGreaterThan)
|
||||
{
|
||||
EXPECT_EQ(math::step(0.5f, 0.6f), 1.0f);
|
||||
}
|
||||
|
||||
TEST(math_base, StepExact)
|
||||
{
|
||||
EXPECT_EQ(math::step(0.5f, 0.5f), 1.0f);
|
||||
}
|
||||
|
||||
TEST(math_base, Midpoint)
|
||||
{
|
||||
EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f);
|
||||
|
|
|
@ -314,7 +314,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb)
|
|||
case SPACE_ACTION: {
|
||||
SpaceAction *saction = (SpaceAction *)sl;
|
||||
|
||||
/* We totally reinit the view for the Action Editor,
|
||||
/* We totally reinitialize the view for the Action Editor,
|
||||
* as some old instances had some weird cruft set. */
|
||||
region->v2d.tot.xmin = -20.0f;
|
||||
region->v2d.tot.ymin = float(-area->winy) / 3.0f;
|
||||
|
|
|
@ -554,11 +554,10 @@ static bNodeTree *add_realize_node_tree(Main *bmain)
|
|||
{
|
||||
bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree");
|
||||
|
||||
node_tree->tree_interface.add_socket("Geometry",
|
||||
"",
|
||||
"NodeSocketGeometry",
|
||||
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
|
||||
nullptr);
|
||||
node_tree->tree_interface.add_socket(
|
||||
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
|
||||
node_tree->tree_interface.add_socket(
|
||||
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
|
||||
bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT);
|
||||
group_input->locx = -400.0f;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
/* Define macros in `DNA_genfile.h`. */
|
||||
#define DNA_GENFILE_VERSIONING_MACROS
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
|
@ -55,6 +56,7 @@
|
|||
#include "BKE_main.hh"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_mesh_legacy_convert.hh"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_tracking.h"
|
||||
|
@ -349,6 +351,51 @@ static void versioning_replace_splitviewer(bNodeTree *ntree)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit NLA tweakmode when the AnimData struct has insufficient information.
|
||||
*
|
||||
* When NLA tweakmode is enabled, Blender expects certain pointers to be set up
|
||||
* correctly, and if that fails, can crash. This function ensures that
|
||||
* everything is consistent, by exiting tweakmode everywhere there's missing
|
||||
* pointers.
|
||||
*
|
||||
* This shouldn't happen, but the example blend file attached to #119615 needs
|
||||
* this.
|
||||
*/
|
||||
static void version_nla_tweakmode_incomplete(Main *bmain)
|
||||
{
|
||||
bool any_valid_tweakmode_left = false;
|
||||
|
||||
ID *id;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
if (!adt || !(adt->flag & ADT_NLA_EDIT_ON)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (adt->act_track && adt->actstrip) {
|
||||
/* Expected case. */
|
||||
any_valid_tweakmode_left = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Not enough info in the blend file to reliably stay in tweak mode. This is the most important
|
||||
* part of this versioning code, as it prevents future nullptr access. */
|
||||
BKE_nla_tweakmode_exit(adt);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
if (any_valid_tweakmode_left) {
|
||||
/* There are still NLA strips correctly in tweak mode. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing is in a valid tweakmode, so just disable the corresponding flags on all scenes. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->flag &= ~SCE_NLA_EDIT_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void do_versions_after_linking_400(FileData *fd, Main *bmain)
|
||||
{
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 9)) {
|
||||
|
@ -443,6 +490,10 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 401, 23)) {
|
||||
version_nla_tweakmode_incomplete(bmain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
@ -3005,6 +3056,20 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 10)) {
|
||||
if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "int", "gtao_resolution")) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.gtao_resolution = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 11)) {
|
||||
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
|
||||
light->shadow_resolution_scale = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -531,7 +531,10 @@ void do_versions_after_setup(Main *new_bmain, BlendFileReadReport *reports)
|
|||
BKE_lib_override_library_main_hierarchy_root_ensure(new_bmain);
|
||||
}
|
||||
|
||||
if (!blendfile_or_libraries_versions_atleast(new_bmain, 401, 2)) {
|
||||
if (!blendfile_or_libraries_versions_atleast(new_bmain, 402, 22)) {
|
||||
/* Initial auto smooth versioning started at (401, 2), but a bug caused the legacy flag to not
|
||||
* be cleared, so it is re-run in a later version when the bug is fixed and the versioning has
|
||||
* been made idempotent. */
|
||||
BKE_main_mesh_legacy_convert_auto_smooth(*new_bmain);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -481,7 +481,7 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(275, 2)) {
|
||||
userdef->ndof_deadzone = 0.1;
|
||||
userdef->ndof_deadzone = 0.0;
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(275, 4)) {
|
||||
|
|
|
@ -203,8 +203,9 @@ class MemoryBuffer {
|
|||
}
|
||||
|
||||
/* Equivalent to the GLSL texture() function with bilinear interpolation and extended boundary
|
||||
* conditions. The coordinates are thus expected to have half-pixels offsets. For float buffers,
|
||||
* the green and green channels will be zero and the alpha will be one. */
|
||||
* conditions. The coordinates are thus expected to have half-pixels offsets. A float4 is always
|
||||
* returned regardless of the number of channels of the buffer, the remaining channels will be
|
||||
* initialized with the template float4(0, 0, 0, 1). */
|
||||
float4 texture_bilinear_extend(float2 coordinates) const
|
||||
{
|
||||
const int2 size = int2(get_width(), get_height());
|
||||
|
|
|
@ -299,7 +299,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
|
|||
graph_id_tag_update(deg_graph->bmain,
|
||||
deg_graph,
|
||||
°_graph->scene->id,
|
||||
ID_RECALC_BASE_FLAGS,
|
||||
ID_RECALC_BASE_FLAGS | ID_RECALC_HIERARCHY,
|
||||
deg::DEG_UPDATE_SOURCE_RELATIONS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,10 +182,8 @@ set(SRC
|
|||
engines/gpencil/gpencil_antialiasing.cc
|
||||
engines/gpencil/gpencil_cache_utils.cc
|
||||
engines/gpencil/gpencil_draw_data.cc
|
||||
engines/gpencil/gpencil_engine.cc
|
||||
engines/gpencil/gpencil_engine_c.cc
|
||||
engines/gpencil/gpencil_render.cc
|
||||
engines/gpencil/gpencil_shader.cc
|
||||
engines/gpencil/gpencil_shader_c.cc
|
||||
engines/gpencil/gpencil_shader_fx.cc
|
||||
engines/select/select_draw_utils.cc
|
||||
|
@ -306,14 +304,7 @@ set(SRC
|
|||
engines/eevee_next/eevee_volume.hh
|
||||
engines/eevee_next/eevee_world.hh
|
||||
engines/external/external_engine.h
|
||||
engines/gpencil/gpencil_antialiasing.hh
|
||||
engines/gpencil/gpencil_engine.h
|
||||
engines/gpencil/gpencil_layer.hh
|
||||
engines/gpencil/gpencil_light.hh
|
||||
engines/gpencil/gpencil_material.hh
|
||||
engines/gpencil/gpencil_object.hh
|
||||
engines/gpencil/gpencil_shader.hh
|
||||
engines/gpencil/gpencil_vfx.hh
|
||||
engines/image/image_batches.hh
|
||||
engines/image/image_buffer_cache.hh
|
||||
engines/image/image_drawing_mode.hh
|
||||
|
@ -507,6 +498,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_film_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_film_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_filter_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_forward_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_gbuffer_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl
|
||||
|
@ -520,6 +512,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_hiz_update_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_scan_lib.glsl
|
||||
|
@ -538,6 +531,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_world_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_volume_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lookdev_display_frag.glsl
|
||||
|
@ -567,11 +561,11 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_ray_types_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_convolve_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_irradiance_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_mapping_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_update_irradiance_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_renderpass_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
|
||||
|
@ -717,9 +711,7 @@ set(GLSL_SRC
|
|||
intern/draw_shader_shared.h
|
||||
|
||||
engines/gpencil/shaders/gpencil_frag.glsl
|
||||
engines/gpencil/shaders/grease_pencil_frag.glsl
|
||||
engines/gpencil/shaders/gpencil_vert.glsl
|
||||
engines/gpencil/shaders/grease_pencil_vert.glsl
|
||||
engines/gpencil/shaders/gpencil_antialiasing_frag.glsl
|
||||
engines/gpencil/shaders/gpencil_antialiasing_vert.glsl
|
||||
engines/gpencil/shaders/gpencil_common_lib.glsl
|
||||
|
@ -727,7 +719,6 @@ set(GLSL_SRC
|
|||
engines/gpencil/shaders/gpencil_mask_invert_frag.glsl
|
||||
engines/gpencil/shaders/gpencil_depth_merge_frag.glsl
|
||||
engines/gpencil/shaders/gpencil_depth_merge_vert.glsl
|
||||
engines/gpencil/shaders/grease_pencil_depth_merge_vert.glsl
|
||||
engines/gpencil/shaders/gpencil_vfx_frag.glsl
|
||||
|
||||
engines/gpencil/gpencil_defines.h
|
||||
|
@ -774,6 +765,8 @@ set(GLSL_SRC
|
|||
engines/overlay/shaders/overlay_edit_curve_handle_vert.glsl
|
||||
engines/overlay/shaders/overlay_edit_curve_handle_vert_no_geom.glsl
|
||||
engines/overlay/shaders/overlay_edit_curve_point_vert.glsl
|
||||
engines/overlay/shaders/overlay_edit_curves_handle_frag.glsl
|
||||
engines/overlay/shaders/overlay_edit_curves_handle_vert.glsl
|
||||
engines/overlay/shaders/overlay_edit_curve_wire_vert.glsl
|
||||
engines/overlay/shaders/overlay_edit_gpencil_canvas_vert.glsl
|
||||
engines/overlay/shaders/overlay_edit_gpencil_guide_vert.glsl
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
# pragma once
|
||||
#endif
|
||||
|
||||
#ifndef SQUARE
|
||||
# define SQUARE(x) ((x) * (x))
|
||||
#endif
|
||||
|
||||
/* Look Up Tables. */
|
||||
#define LUT_WORKGROUP_SIZE 16
|
||||
|
||||
|
@ -30,14 +34,17 @@
|
|||
#define CULLING_TILE_GROUP_SIZE 256
|
||||
|
||||
/* Reflection Probes. */
|
||||
#define SPHERE_PROBE_REMAP_GROUP_SIZE 32
|
||||
#define SPHERE_PROBE_GROUP_SIZE 16
|
||||
#define SPHERE_PROBE_SELECT_GROUP_SIZE 64
|
||||
#define SPHERE_PROBE_MIPMAP_LEVELS 5
|
||||
#define SPHERE_PROBE_SH_GROUP_SIZE 512
|
||||
#define SPHERE_PROBE_SH_GROUP_SIZE 256
|
||||
#define SPHERE_PROBE_SH_SAMPLES_PER_GROUP 64
|
||||
/* Must be power of two for correct partitioning. */
|
||||
#define SPHERE_PROBE_ATLAS_MAX_SUBDIV 10
|
||||
#define SPHERE_PROBE_ATLAS_RES (1 << SPHERE_PROBE_ATLAS_MAX_SUBDIV)
|
||||
/* Maximum number of thread-groups dispatched for remapping a probe to octahedral mapping. */
|
||||
#define SPHERE_PROBE_MAX_HARMONIC SQUARE(SPHERE_PROBE_ATLAS_RES / SPHERE_PROBE_REMAP_GROUP_SIZE)
|
||||
/* Start and end value for mixing sphere probe and volume probes. */
|
||||
#define SPHERE_PROBE_MIX_START_ROUGHNESS 0.7
|
||||
#define SPHERE_PROBE_MIX_END_ROUGHNESS 0.9
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue