Anim: run bezier handle calculation in parallel #119388

Merged
Christoph Lendenfeld merged 21 commits from ChrisLend/blender:thread_recalc_handles into main 2024-05-07 10:43:03 +02:00
354 changed files with 194212 additions and 5651 deletions
Showing only changes of commit aa3e8322a4 - Show all commits

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
{%- extends "!page.html" -%}
{%- block footer -%}
{{ super() }}
{%- include "components/footer_contribute.html" -%}
{%- endblock footer -%}

View File

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

View File

@ -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
extern/README vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -113,15 +113,18 @@ BVHMetal::BVHMetal(const BVHParams &params_,
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);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ __all__ = (
"app",
"context",
"data",
"msgbus",
"ops",
"path",
"props",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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).
*/

View File

@ -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
* \{ */

View File

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

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -299,7 +299,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
graph_id_tag_update(deg_graph->bmain,
deg_graph,
&deg_graph->scene->id,
ID_RECALC_BASE_FLAGS,
ID_RECALC_BASE_FLAGS | ID_RECALC_HIERARCHY,
deg::DEG_UPDATE_SOURCE_RELATIONS);
}
}

View File

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

View File

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