Add Chromium-only Blender WebEngine parity work
This commit is contained in:
81
blender-5.2.0/intern/CMakeLists.txt
Normal file
81
blender-5.2.0/intern/CMakeLists.txt
Normal file
@@ -0,0 +1,81 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_subdirectory(atomic)
|
||||
add_subdirectory(clog)
|
||||
add_subdirectory(ghost)
|
||||
add_subdirectory(guardedalloc)
|
||||
add_subdirectory(libmv)
|
||||
add_subdirectory(memutil)
|
||||
add_subdirectory(opensubdiv)
|
||||
add_subdirectory(profile)
|
||||
add_subdirectory(mikktspace)
|
||||
add_subdirectory(eigen)
|
||||
add_subdirectory(sky)
|
||||
|
||||
add_subdirectory(openvdb)
|
||||
|
||||
if(WITH_MOD_REMESH)
|
||||
add_subdirectory(dualcon)
|
||||
endif()
|
||||
|
||||
if(WITH_IK_SOLVER)
|
||||
add_subdirectory(iksolver)
|
||||
endif()
|
||||
|
||||
if(WITH_IK_ITASC)
|
||||
add_subdirectory(itasc)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES)
|
||||
add_subdirectory(cycles)
|
||||
endif()
|
||||
|
||||
if(WITH_BULLET)
|
||||
add_subdirectory(rigidbody)
|
||||
endif()
|
||||
|
||||
|
||||
if(WIN32)
|
||||
# Only windows needs utf16 converter.
|
||||
add_subdirectory(utfconv)
|
||||
|
||||
# Only used for Windows for now.
|
||||
add_subdirectory(uriconvert)
|
||||
endif()
|
||||
|
||||
if(WITH_MOD_FLUID)
|
||||
add_subdirectory(mantaflow)
|
||||
endif()
|
||||
|
||||
if(WITH_UV_SLIM)
|
||||
add_subdirectory(slim)
|
||||
endif()
|
||||
|
||||
if(WITH_QUADRIFLOW)
|
||||
add_subdirectory(quadriflow)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_subdirectory(libc_compat)
|
||||
endif()
|
||||
|
||||
if(WITH_RENDERDOC)
|
||||
add_subdirectory(renderdoc_dynload)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
# Important this comes after "ghost" as it uses includes defined by GHOST's CMake.
|
||||
if(WITH_GHOST_WAYLAND AND WITH_GHOST_WAYLAND_DYNLOAD)
|
||||
add_subdirectory(wayland_dynload)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_DRACO)
|
||||
add_subdirectory(draco_bridge)
|
||||
endif()
|
||||
|
||||
if (WITH_MESHOPTIMIZER)
|
||||
add_subdirectory(meshoptimizer_bridge)
|
||||
endif()
|
||||
39
blender-5.2.0/intern/atomic/CMakeLists.txt
Normal file
39
blender-5.2.0/intern/atomic/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
# SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
set(LIB
|
||||
)
|
||||
|
||||
add_library(bf_intern_atomic INTERFACE)
|
||||
|
||||
target_include_directories(bf_intern_atomic INTERFACE .)
|
||||
add_library(bf::intern::atomic ALIAS bf_intern_atomic)
|
||||
|
||||
set(SRC
|
||||
atomic_ops.h
|
||||
intern/atomic_ops_ext.h
|
||||
intern/atomic_ops_msvc.h
|
||||
intern/atomic_ops_unix.h
|
||||
intern/atomic_ops_utils.h
|
||||
)
|
||||
target_sources(bf_intern_atomic PRIVATE ${SRC})
|
||||
blender_source_group(bf_intern_atomic ${SRC})
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_SRC
|
||||
tests/atomic_test.cc
|
||||
)
|
||||
set(TEST_INC
|
||||
)
|
||||
set(TEST_LIB
|
||||
PRIVATE bf_intern_atomic
|
||||
)
|
||||
blender_add_test_executable(atomic "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
|
||||
endif()
|
||||
150
blender-5.2.0/intern/atomic/atomic_ops.h
Normal file
150
blender-5.2.0/intern/atomic/atomic_ops.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Original code from jemalloc with this license:
|
||||
*
|
||||
* Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>.
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is adapted from jemalloc.
|
||||
* Modifications Copyright (C) 2016 Blender Foundation
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_atomic
|
||||
*
|
||||
* \brief Provides wrapper around system-specific atomic primitives,
|
||||
* and some extensions (faked-atomic operations over float numbers).
|
||||
*/
|
||||
|
||||
#ifndef __ATOMIC_OPS_H__
|
||||
#define __ATOMIC_OPS_H__
|
||||
|
||||
#include "intern/atomic_ops_utils.h" /* IWYU pragma: export */
|
||||
|
||||
/******************************************************************************/
|
||||
/* Function prototypes. */
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new);
|
||||
ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v);
|
||||
ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v);
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_add_and_fetch_int64(int64_t *p, int64_t x);
|
||||
ATOMIC_INLINE int64_t atomic_sub_and_fetch_int64(int64_t *p, int64_t x);
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x);
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x);
|
||||
ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new);
|
||||
ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v);
|
||||
ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v);
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new);
|
||||
ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v);
|
||||
ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v);
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x);
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x);
|
||||
ATOMIC_INLINE int32_t atomic_sub_and_fetch_int32(int32_t *p, int32_t x);
|
||||
ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new);
|
||||
ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v);
|
||||
ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v);
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_add_int32(int32_t *p, int32_t x);
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_or_int32(int32_t *p, int32_t x);
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_and_int32(int32_t *p, int32_t x);
|
||||
|
||||
ATOMIC_INLINE int16_t atomic_fetch_and_or_int16(int16_t *p, int16_t b);
|
||||
ATOMIC_INLINE int16_t atomic_fetch_and_and_int16(int16_t *p, int16_t b);
|
||||
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b);
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_and_uint8(uint8_t *p, uint8_t b);
|
||||
|
||||
ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b);
|
||||
ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b);
|
||||
|
||||
ATOMIC_INLINE char atomic_fetch_and_or_char(char *p, char b);
|
||||
ATOMIC_INLINE char atomic_fetch_and_and_char(char *p, char b);
|
||||
|
||||
ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new);
|
||||
ATOMIC_INLINE size_t atomic_load_z(const size_t *v);
|
||||
ATOMIC_INLINE void atomic_store_z(size_t *p, size_t v);
|
||||
/* Uses CAS loop, see warning below. */
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_update_max_z(size_t *p, size_t x);
|
||||
|
||||
ATOMIC_INLINE unsigned int atomic_add_and_fetch_u(unsigned int *p, unsigned int x);
|
||||
ATOMIC_INLINE unsigned int atomic_sub_and_fetch_u(unsigned int *p, unsigned int x);
|
||||
ATOMIC_INLINE unsigned int atomic_fetch_and_add_u(unsigned int *p, unsigned int x);
|
||||
ATOMIC_INLINE unsigned int atomic_fetch_and_sub_u(unsigned int *p, unsigned int x);
|
||||
ATOMIC_INLINE unsigned int atomic_cas_u(unsigned int *v, unsigned int old, unsigned int _new);
|
||||
|
||||
ATOMIC_INLINE void *atomic_cas_ptr(void **v, void *old, void *_new);
|
||||
ATOMIC_INLINE void *atomic_load_ptr(void *const *v);
|
||||
ATOMIC_INLINE void atomic_store_ptr(void **p, void *v);
|
||||
|
||||
ATOMIC_INLINE float atomic_cas_float(float *v, float old, float _new);
|
||||
|
||||
/* WARNING! Float 'atomics' are really faked ones, those are actually closer to some kind of
|
||||
* spinlock-sync'ed operation, which means they are only efficient if collisions are highly
|
||||
* unlikely (i.e. if probability of two threads working on the same pointer at the same time is
|
||||
* very low). */
|
||||
ATOMIC_INLINE float atomic_add_and_fetch_fl(float *p, const float x);
|
||||
|
||||
/******************************************************************************/
|
||||
/* Include system-dependent implementations. */
|
||||
|
||||
/* Note that we are using _unix flavor as fallback here
|
||||
* (it will raise precompiler errors as needed). */
|
||||
#if defined(_MSC_VER)
|
||||
# include "intern/atomic_ops_msvc.h" /* IWYU pragma: export */
|
||||
#else
|
||||
# include "intern/atomic_ops_unix.h" /* IWYU pragma: export */
|
||||
#endif
|
||||
|
||||
/* Include 'fake' atomic extensions, built over real atomic primitives. */
|
||||
#include "intern/atomic_ops_ext.h" /* IWYU pragma: export */
|
||||
|
||||
#endif /* __ATOMIC_OPS_H__ */
|
||||
248
blender-5.2.0/intern/atomic/intern/atomic_ops_ext.h
Normal file
248
blender-5.2.0/intern/atomic/intern/atomic_ops_ext.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Original code from jemalloc with this license:
|
||||
*
|
||||
* Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>.
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is adapted from jemalloc.
|
||||
* Modifications Copyright (C) 2016 Blender Foundation
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_atomic
|
||||
*/
|
||||
|
||||
#ifndef __ATOMIC_OPS_EXT_H__
|
||||
#define __ATOMIC_OPS_EXT_H__
|
||||
|
||||
#include "atomic_ops_utils.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* size_t operations. */
|
||||
ATOMIC_STATIC_ASSERT(sizeof(size_t) == LG_SIZEOF_PTR, "sizeof(size_t) != LG_SIZEOF_PTR");
|
||||
|
||||
ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_cas_uint64((uint64_t *)v, (uint64_t)old, (uint64_t)_new);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_cas_uint32((uint32_t *)v, (uint32_t)old, (uint32_t)_new);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_load_z(const size_t *v)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_load_uint64((const uint64_t *)v);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_load_uint32((const uint32_t *)v);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_z(size_t *p, size_t v)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
atomic_store_uint64((uint64_t *)p, v);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
atomic_store_uint32((uint32_t *)p, v);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_update_max_z(size_t *p, size_t x)
|
||||
{
|
||||
size_t prev_value;
|
||||
while ((prev_value = *p) < x) {
|
||||
if (atomic_cas_z(p, prev_value, x) == prev_value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prev_value;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* unsigned operations. */
|
||||
ATOMIC_STATIC_ASSERT(sizeof(unsigned int) == LG_SIZEOF_INT,
|
||||
"sizeof(unsigned int) != LG_SIZEOF_INT");
|
||||
|
||||
ATOMIC_INLINE unsigned int atomic_add_and_fetch_u(unsigned int *p, unsigned int x)
|
||||
{
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned int)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned int)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned int atomic_sub_and_fetch_u(unsigned int *p, unsigned int x)
|
||||
{
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned int)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned int)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned int atomic_fetch_and_add_u(unsigned int *p, unsigned int x)
|
||||
{
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned int)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned int)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned int atomic_fetch_and_sub_u(unsigned int *p, unsigned int x)
|
||||
{
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned int)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned int)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned int atomic_cas_u(unsigned int *v, unsigned int old, unsigned int _new)
|
||||
{
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned int)atomic_cas_uint64((uint64_t *)v, (uint64_t)old, (uint64_t)_new);
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned int)atomic_cas_uint32((uint32_t *)v, (uint32_t)old, (uint32_t)_new);
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Char operations. */
|
||||
ATOMIC_INLINE char atomic_fetch_and_or_char(char *p, char b)
|
||||
{
|
||||
return (char)atomic_fetch_and_or_uint8((uint8_t *)p, (uint8_t)b);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE char atomic_fetch_and_and_char(char *p, char b)
|
||||
{
|
||||
return (char)atomic_fetch_and_and_uint8((uint8_t *)p, (uint8_t)b);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Pointer operations. */
|
||||
|
||||
ATOMIC_INLINE void *atomic_cas_ptr(void **v, void *old, void *_new)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (void *)atomic_cas_uint64((uint64_t *)v, *(uint64_t *)&old, *(uint64_t *)&_new);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (void *)atomic_cas_uint32((uint32_t *)v, *(uint32_t *)&old, *(uint32_t *)&_new);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void *atomic_load_ptr(void *const *v)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (void *)atomic_load_uint64((const uint64_t *)v);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (void *)atomic_load_uint32((const uint32_t *)v);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_ptr(void **p, void *v)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
atomic_store_uint64((uint64_t *)p, (uint64_t)v);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
atomic_store_uint32((uint32_t *)p, (uint32_t)v);
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* float operations. */
|
||||
ATOMIC_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "sizeof(float) != sizeof(uint32_t)");
|
||||
|
||||
ATOMIC_INLINE float atomic_cas_float(float *v, float old, float _new)
|
||||
{
|
||||
uint32_t ret = atomic_cas_uint32((uint32_t *)v, *(uint32_t *)&old, *(uint32_t *)&_new);
|
||||
return *(float *)&ret;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE float atomic_add_and_fetch_fl(float *p, const float x)
|
||||
{
|
||||
float oldval, newval;
|
||||
uint32_t prevval;
|
||||
|
||||
do { /* Note that since collisions are unlikely, loop will nearly always run once. */
|
||||
oldval = *p;
|
||||
newval = oldval + x;
|
||||
prevval = atomic_cas_uint32((uint32_t *)p, *(uint32_t *)(&oldval), *(uint32_t *)(&newval));
|
||||
} while (_ATOMIC_UNLIKELY(prevval != *(uint32_t *)(&oldval)));
|
||||
|
||||
return newval;
|
||||
}
|
||||
|
||||
#endif /* __ATOMIC_OPS_EXT_H__ */
|
||||
284
blender-5.2.0/intern/atomic/intern/atomic_ops_msvc.h
Normal file
284
blender-5.2.0/intern/atomic/intern/atomic_ops_msvc.h
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Adopted from jemalloc with this license:
|
||||
*
|
||||
* Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>.
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_atomic
|
||||
*/
|
||||
|
||||
#ifndef __ATOMIC_OPS_MSVC_H__
|
||||
#define __ATOMIC_OPS_MSVC_H__
|
||||
|
||||
#include "atomic_ops_utils.h"
|
||||
|
||||
#define NOGDI
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <intrin.h>
|
||||
#include <windows.h>
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
|
||||
#endif
|
||||
|
||||
/* TODO(sergey): On x64 platform both read and write of a variable aligned to its type size is
|
||||
* atomic, so in theory it is possible to avoid memory barrier and gain performance. The downside
|
||||
* of that would be that it will impose requirement to value which is being operated on. */
|
||||
#define __atomic_impl_load_generic(v) (MemoryBarrier(), *(v))
|
||||
#define __atomic_impl_store_generic(p, v) \
|
||||
do { \
|
||||
*(p) = (v); \
|
||||
MemoryBarrier(); \
|
||||
} while (0)
|
||||
|
||||
/* 64-bit operations. */
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
|
||||
{
|
||||
return InterlockedCompareExchange64((int64_t *)v, _new, old);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v)
|
||||
{
|
||||
return __atomic_impl_load_generic(v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v)
|
||||
{
|
||||
__atomic_impl_store_generic(p, v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int64_t atomic_add_and_fetch_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64(p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_sub_and_fetch_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64(p, -x) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new)
|
||||
{
|
||||
return InterlockedCompareExchange64(v, _new, old);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v)
|
||||
{
|
||||
return __atomic_impl_load_generic(v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v)
|
||||
{
|
||||
__atomic_impl_store_generic(p, v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64(p, -x);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* 32-bit operations. */
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd(p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd(p, -((int32_t)x)) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new)
|
||||
{
|
||||
return InterlockedCompareExchange((long *)v, _new, old);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v)
|
||||
{
|
||||
return __atomic_impl_load_generic(v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v)
|
||||
{
|
||||
__atomic_impl_store_generic(p, v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedOr((long *)p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedAnd((long *)p, x);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd((long *)p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_sub_and_fetch_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd((long *)p, -x) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new)
|
||||
{
|
||||
return InterlockedCompareExchange((long *)v, _new, old);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v)
|
||||
{
|
||||
return __atomic_impl_load_generic(v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v)
|
||||
{
|
||||
__atomic_impl_store_generic(p, v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_add_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd((long *)p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_or_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return InterlockedOr((long *)p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_and_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return InterlockedAnd((long *)p, x);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* 16-bit operations. */
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int16_t atomic_fetch_and_or_int16(int16_t *p, int16_t x)
|
||||
{
|
||||
return InterlockedOr16((short *)p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int16_t atomic_fetch_and_and_int16(int16_t *p, int16_t x)
|
||||
{
|
||||
return InterlockedAnd16((short *)p, x);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* 8-bit operations. */
|
||||
|
||||
/* Unsigned */
|
||||
#pragma intrinsic(_InterlockedAnd8)
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_and_uint8(uint8_t *p, uint8_t b)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
return InterlockedAnd8((char *)p, (char)b);
|
||||
#else
|
||||
return _InterlockedAnd8((char *)p, (char)b);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma intrinsic(_InterlockedOr8)
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
return InterlockedOr8((char *)p, (char)b);
|
||||
#else
|
||||
return _InterlockedOr8((char *)p, (char)b);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
#pragma intrinsic(_InterlockedAnd8)
|
||||
ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
return InterlockedAnd8((char *)p, (char)b);
|
||||
#else
|
||||
return _InterlockedAnd8((char *)p, (char)b);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma intrinsic(_InterlockedOr8)
|
||||
ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b)
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
return InterlockedOr8((char *)p, (char)b);
|
||||
#else
|
||||
return _InterlockedOr8((char *)p, (char)b);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef __atomic_impl_load_generic
|
||||
#undef __atomic_impl_store_generic
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif /* __ATOMIC_OPS_MSVC_H__ */
|
||||
690
blender-5.2.0/intern/atomic/intern/atomic_ops_unix.h
Normal file
690
blender-5.2.0/intern/atomic/intern/atomic_ops_unix.h
Normal file
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* Original code from jemalloc with this license:
|
||||
*
|
||||
* Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>.
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is adapted from jemalloc.
|
||||
* Modifications Copyright (C) 2016 Blender Foundation
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_atomic
|
||||
*/
|
||||
|
||||
#ifndef __ATOMIC_OPS_UNIX_H__
|
||||
#define __ATOMIC_OPS_UNIX_H__
|
||||
|
||||
#include "atomic_ops_utils.h"
|
||||
|
||||
#if defined(__arm__) || defined(__riscv)
|
||||
/* Attempt to fix compilation error on Debian armel and RISC-V kernels.
|
||||
* Both architectures do have both 32 and 64bit atomics, however
|
||||
* its gcc doesn't have __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n defined.
|
||||
*/
|
||||
# define JE_FORCE_SYNC_COMPARE_AND_SWAP_1
|
||||
# define JE_FORCE_SYNC_COMPARE_AND_SWAP_2
|
||||
# define JE_FORCE_SYNC_COMPARE_AND_SWAP_4
|
||||
# define JE_FORCE_SYNC_COMPARE_AND_SWAP_8
|
||||
#endif
|
||||
|
||||
/* Define the `ATOMIC_FORCE_USE_FALLBACK` to force lock-based fallback implementation to be used
|
||||
* (even on platforms where there is native implementation available via compiler.
|
||||
* Useful for development purposes. */
|
||||
#undef ATOMIC_FORCE_USE_FALLBACK
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Spin-lock implementation
|
||||
*
|
||||
* Used to implement atomics on unsupported platforms.
|
||||
* The spin implementation is shared for all platforms to make sure it compiles and tested.
|
||||
* \{ */
|
||||
|
||||
typedef struct AtomicSpinLock {
|
||||
volatile int lock;
|
||||
|
||||
/* Pad the structure size to a cache-line, to avoid unwanted sharing with other data. */
|
||||
int pad[32 - sizeof(int)];
|
||||
} __attribute__((aligned(32))) AtomicSpinLock;
|
||||
|
||||
ATOMIC_INLINE void atomic_spin_lock(volatile AtomicSpinLock *lock)
|
||||
{
|
||||
while (__sync_lock_test_and_set(&lock->lock, 1)) {
|
||||
while (lock->lock) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_spin_unlock(volatile AtomicSpinLock *lock)
|
||||
{
|
||||
__sync_lock_release(&lock->lock);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Common part of x64 implementation
|
||||
* \{ */
|
||||
|
||||
/* TODO(sergey): On x64 platform both read and write of a variable aligned to its type size is
|
||||
* atomic, so in theory it is possible to avoid memory barrier and gain performance. The downside
|
||||
* of that would be that it will impose requirement to value which is being operated on. */
|
||||
#define __atomic_impl_load_generic(v) (__sync_synchronize(), *(v))
|
||||
#define __atomic_impl_store_generic(p, v) \
|
||||
do { \
|
||||
*(p) = (v); \
|
||||
__sync_synchronize(); \
|
||||
} while (0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Common part of locking fallback implementation
|
||||
* \{ */
|
||||
|
||||
/* Global lock, shared by all atomic operations implementations.
|
||||
*
|
||||
* Could be split into per-size locks, although added complexity and being more error-proone does
|
||||
* not seem to worth it for a fall-back implementation. */
|
||||
static _ATOMIC_MAYBE_UNUSED AtomicSpinLock _atomic_global_lock = {0};
|
||||
|
||||
#define ATOMIC_LOCKING_OP_AND_FETCH_DEFINE(_type, _op_name, _op) \
|
||||
ATOMIC_INLINE _type##_t atomic_##_op_name##_and_fetch_##_type(_type##_t *p, _type##_t x) \
|
||||
{ \
|
||||
atomic_spin_lock(&_atomic_global_lock); \
|
||||
const _type##_t original_value = *(p); \
|
||||
const _type##_t new_value = original_value _op(x); \
|
||||
*(p) = new_value; \
|
||||
atomic_spin_unlock(&_atomic_global_lock); \
|
||||
return new_value; \
|
||||
}
|
||||
|
||||
#define ATOMIC_LOCKING_FETCH_AND_OP_DEFINE(_type, _op_name, _op) \
|
||||
ATOMIC_INLINE _type##_t atomic_fetch_and_##_op_name##_##_type(_type##_t *p, _type##_t x) \
|
||||
{ \
|
||||
atomic_spin_lock(&_atomic_global_lock); \
|
||||
const _type##_t original_value = *(p); \
|
||||
*(p) = original_value _op(x); \
|
||||
atomic_spin_unlock(&_atomic_global_lock); \
|
||||
return original_value; \
|
||||
}
|
||||
|
||||
#define ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(_type) \
|
||||
ATOMIC_LOCKING_OP_AND_FETCH_DEFINE(_type, add, +)
|
||||
|
||||
#define ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(_type) \
|
||||
ATOMIC_LOCKING_OP_AND_FETCH_DEFINE(_type, sub, -)
|
||||
|
||||
#define ATOMIC_LOCKING_FETCH_AND_ADD_DEFINE(_type) \
|
||||
ATOMIC_LOCKING_FETCH_AND_OP_DEFINE(_type, add, +)
|
||||
|
||||
#define ATOMIC_LOCKING_FETCH_AND_SUB_DEFINE(_type) \
|
||||
ATOMIC_LOCKING_FETCH_AND_OP_DEFINE(_type, sub, -)
|
||||
|
||||
#define ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(_type) ATOMIC_LOCKING_FETCH_AND_OP_DEFINE(_type, or, |)
|
||||
|
||||
#define ATOMIC_LOCKING_FETCH_AND_AND_DEFINE(_type) \
|
||||
ATOMIC_LOCKING_FETCH_AND_OP_DEFINE(_type, and, &)
|
||||
|
||||
#define ATOMIC_LOCKING_CAS_DEFINE(_type) \
|
||||
ATOMIC_INLINE _type##_t atomic_cas_##_type(_type##_t *v, _type##_t old, _type##_t _new) \
|
||||
{ \
|
||||
atomic_spin_lock(&_atomic_global_lock); \
|
||||
const _type##_t original_value = *v; \
|
||||
if (*v == old) { \
|
||||
*v = _new; \
|
||||
} \
|
||||
atomic_spin_unlock(&_atomic_global_lock); \
|
||||
return original_value; \
|
||||
}
|
||||
|
||||
#define ATOMIC_LOCKING_LOAD_DEFINE(_type) \
|
||||
ATOMIC_INLINE _type##_t atomic_load_##_type(const _type##_t *v) \
|
||||
{ \
|
||||
atomic_spin_lock(&_atomic_global_lock); \
|
||||
const _type##_t value = *v; \
|
||||
atomic_spin_unlock(&_atomic_global_lock); \
|
||||
return value; \
|
||||
}
|
||||
|
||||
#define ATOMIC_LOCKING_STORE_DEFINE(_type) \
|
||||
ATOMIC_INLINE void atomic_store_##_type(_type##_t *p, const _type##_t v) \
|
||||
{ \
|
||||
atomic_spin_lock(&_atomic_global_lock); \
|
||||
*p = v; \
|
||||
atomic_spin_unlock(&_atomic_global_lock); \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name 64-bit operations
|
||||
* \{ */
|
||||
|
||||
#if !defined(ATOMIC_FORCE_USE_FALLBACK) && \
|
||||
(defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8))
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_add_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_sub_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_fetch_and_add(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_fetch_and_sub(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
|
||||
{
|
||||
return __sync_val_compare_and_swap(v, old, _new);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v)
|
||||
{
|
||||
return __atomic_load_n(v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v)
|
||||
{
|
||||
__atomic_store(p, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int64_t atomic_add_and_fetch_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return __sync_add_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_sub_and_fetch_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return __sync_sub_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return __sync_fetch_and_add(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return __sync_fetch_and_sub(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new)
|
||||
{
|
||||
return __sync_val_compare_and_swap(v, old, _new);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v)
|
||||
{
|
||||
return __atomic_load_n(v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v)
|
||||
{
|
||||
__atomic_store(p, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#elif !defined(ATOMIC_FORCE_USE_FALLBACK) && (defined(__amd64__) || defined(__x86_64__))
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
asm volatile("lock; xaddq %0, %1;"
|
||||
: "+r"(x), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
x = (uint64_t)(-(int64_t)x);
|
||||
asm volatile("lock; xaddq %0, %1;"
|
||||
: "+r"(x), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return atomic_fetch_and_add_uint64(p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return atomic_fetch_and_sub_uint64(p, x) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
|
||||
{
|
||||
uint64_t ret;
|
||||
asm volatile("lock; cmpxchgq %2,%1" : "=a"(ret), "+m"(*v) : "r"(_new), "0"(old) : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v)
|
||||
{
|
||||
return __atomic_impl_load_generic(v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v)
|
||||
{
|
||||
__atomic_impl_store_generic(p, v);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
asm volatile("lock; xaddq %0, %1;"
|
||||
: "+r"(x), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
x = -x;
|
||||
asm volatile("lock; xaddq %0, %1;"
|
||||
: "+r"(x), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_add_and_fetch_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return atomic_fetch_and_add_int64(p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_sub_and_fetch_int64(int64_t *p, int64_t x)
|
||||
{
|
||||
return atomic_fetch_and_sub_int64(p, x) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new)
|
||||
{
|
||||
int64_t ret;
|
||||
asm volatile("lock; cmpxchgq %2,%1" : "=a"(ret), "+m"(*v) : "r"(_new), "0"(old) : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v)
|
||||
{
|
||||
return __atomic_impl_load_generic(v);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v)
|
||||
{
|
||||
__atomic_impl_store_generic(p, v);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Unsigned */
|
||||
|
||||
ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(uint64)
|
||||
ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(uint64)
|
||||
|
||||
ATOMIC_LOCKING_FETCH_AND_ADD_DEFINE(uint64)
|
||||
ATOMIC_LOCKING_FETCH_AND_SUB_DEFINE(uint64)
|
||||
|
||||
ATOMIC_LOCKING_CAS_DEFINE(uint64)
|
||||
|
||||
ATOMIC_LOCKING_LOAD_DEFINE(uint64)
|
||||
ATOMIC_LOCKING_STORE_DEFINE(uint64)
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(int64)
|
||||
ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(int64)
|
||||
|
||||
ATOMIC_LOCKING_FETCH_AND_ADD_DEFINE(int64)
|
||||
ATOMIC_LOCKING_FETCH_AND_SUB_DEFINE(int64)
|
||||
|
||||
ATOMIC_LOCKING_CAS_DEFINE(int64)
|
||||
|
||||
ATOMIC_LOCKING_LOAD_DEFINE(int64)
|
||||
ATOMIC_LOCKING_STORE_DEFINE(int64)
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name 32-bit operations
|
||||
* \{ */
|
||||
|
||||
#if !defined(ATOMIC_FORCE_USE_FALLBACK) && \
|
||||
(defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_add_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_sub_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new)
|
||||
{
|
||||
return __sync_val_compare_and_swap(v, old, _new);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v)
|
||||
{
|
||||
return __atomic_load_n(v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v)
|
||||
{
|
||||
__atomic_store(p, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return __sync_add_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_sub_and_fetch_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return __sync_sub_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new)
|
||||
{
|
||||
return __sync_val_compare_and_swap(v, old, _new);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v)
|
||||
{
|
||||
return __atomic_load_n(v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v)
|
||||
{
|
||||
__atomic_store(p, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#elif !defined(ATOMIC_FORCE_USE_FALLBACK) && \
|
||||
(defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
uint32_t ret = x;
|
||||
asm volatile("lock; xaddl %0, %1;"
|
||||
: "+r"(ret), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return ret + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
uint32_t ret = (uint32_t)(-(int32_t)x);
|
||||
asm volatile("lock; xaddl %0, %1;"
|
||||
: "+r"(ret), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return ret - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new)
|
||||
{
|
||||
uint32_t ret;
|
||||
asm volatile("lock; cmpxchgl %2,%1" : "=a"(ret), "+m"(*v) : "r"(_new), "0"(old) : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v)
|
||||
{
|
||||
return __atomic_load_n(v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v)
|
||||
{
|
||||
__atomic_store(p, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
int32_t ret = x;
|
||||
asm volatile("lock; xaddl %0, %1;"
|
||||
: "+r"(ret), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return ret + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_sub_and_fetch_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
int32_t ret = -x;
|
||||
asm volatile("lock; xaddl %0, %1;"
|
||||
: "+r"(ret), "=m"(*p) /* Outputs. */
|
||||
: "m"(*p) /* Inputs. */
|
||||
);
|
||||
return ret - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new)
|
||||
{
|
||||
int32_t ret;
|
||||
asm volatile("lock; cmpxchgl %2,%1" : "=a"(ret), "+m"(*v) : "r"(_new), "0"(old) : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v)
|
||||
{
|
||||
return __atomic_load_n(v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v)
|
||||
{
|
||||
__atomic_store(p, &v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Unsigned */
|
||||
|
||||
ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(uint32)
|
||||
ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(uint32)
|
||||
|
||||
ATOMIC_LOCKING_CAS_DEFINE(uint32)
|
||||
|
||||
ATOMIC_LOCKING_LOAD_DEFINE(uint32)
|
||||
ATOMIC_LOCKING_STORE_DEFINE(uint32)
|
||||
|
||||
/* Signed */
|
||||
|
||||
ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(int32)
|
||||
ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(int32)
|
||||
|
||||
ATOMIC_LOCKING_CAS_DEFINE(int32)
|
||||
|
||||
ATOMIC_LOCKING_LOAD_DEFINE(int32)
|
||||
ATOMIC_LOCKING_STORE_DEFINE(int32)
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(ATOMIC_FORCE_USE_FALLBACK) && \
|
||||
(defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_fetch_and_add(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_fetch_and_or(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_fetch_and_and(p, x);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_add_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return __sync_fetch_and_add(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_or_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return __sync_fetch_and_or(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE int32_t atomic_fetch_and_and_int32(int32_t *p, int32_t x)
|
||||
{
|
||||
return __sync_fetch_and_and(p, x);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Unsigned */
|
||||
ATOMIC_LOCKING_FETCH_AND_ADD_DEFINE(uint32)
|
||||
ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(uint32)
|
||||
ATOMIC_LOCKING_FETCH_AND_AND_DEFINE(uint32)
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_LOCKING_FETCH_AND_ADD_DEFINE(int32)
|
||||
ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(int32)
|
||||
ATOMIC_LOCKING_FETCH_AND_AND_DEFINE(int32)
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name 16-bit operations
|
||||
* \{ */
|
||||
|
||||
#if !defined(ATOMIC_FORCE_USE_FALLBACK) && \
|
||||
(defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_2))
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int16_t atomic_fetch_and_and_int16(int16_t *p, int16_t b)
|
||||
{
|
||||
return __sync_fetch_and_and(p, b);
|
||||
}
|
||||
ATOMIC_INLINE int16_t atomic_fetch_and_or_int16(int16_t *p, int16_t b)
|
||||
{
|
||||
return __sync_fetch_and_or(p, b);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
ATOMIC_LOCKING_FETCH_AND_AND_DEFINE(int16)
|
||||
ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(int16)
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name 8-bit operations
|
||||
* \{ */
|
||||
|
||||
#if !defined(ATOMIC_FORCE_USE_FALLBACK) && \
|
||||
(defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_1))
|
||||
|
||||
/* Unsigned */
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_and_uint8(uint8_t *p, uint8_t b)
|
||||
{
|
||||
return __sync_fetch_and_and(p, b);
|
||||
}
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b)
|
||||
{
|
||||
return __sync_fetch_and_or(p, b);
|
||||
}
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b)
|
||||
{
|
||||
return __sync_fetch_and_and(p, b);
|
||||
}
|
||||
ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b)
|
||||
{
|
||||
return __sync_fetch_and_or(p, b);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Unsigned */
|
||||
ATOMIC_LOCKING_FETCH_AND_AND_DEFINE(uint8)
|
||||
ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(uint8)
|
||||
|
||||
/* Signed */
|
||||
ATOMIC_LOCKING_FETCH_AND_AND_DEFINE(int8)
|
||||
ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(int8)
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef __atomic_impl_load_generic
|
||||
#undef __atomic_impl_store_generic
|
||||
|
||||
#undef ATOMIC_LOCKING_OP_AND_FETCH_DEFINE
|
||||
#undef ATOMIC_LOCKING_FETCH_AND_OP_DEFINE
|
||||
#undef ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE
|
||||
#undef ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE
|
||||
#undef ATOMIC_LOCKING_FETCH_AND_ADD_DEFINE
|
||||
#undef ATOMIC_LOCKING_FETCH_AND_SUB_DEFINE
|
||||
#undef ATOMIC_LOCKING_FETCH_AND_OR_DEFINE
|
||||
#undef ATOMIC_LOCKING_FETCH_AND_AND_DEFINE
|
||||
#undef ATOMIC_LOCKING_CAS_DEFINE
|
||||
#undef ATOMIC_LOCKING_LOAD_DEFINE
|
||||
#undef ATOMIC_LOCKING_STORE_DEFINE
|
||||
|
||||
#endif /* __ATOMIC_OPS_UNIX_H__ */
|
||||
131
blender-5.2.0/intern/atomic/intern/atomic_ops_utils.h
Normal file
131
blender-5.2.0/intern/atomic/intern/atomic_ops_utils.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Original code from jemalloc with this license:
|
||||
*
|
||||
* Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>.
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is adapted from jemalloc.
|
||||
* Modifications Copyright (C) 2016 Blender Foundation
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_atomic
|
||||
*/
|
||||
|
||||
#ifndef __ATOMIC_OPS_UTILS_H__
|
||||
#define __ATOMIC_OPS_UTILS_H__
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* little macro so inline keyword works */
|
||||
#if defined(_MSC_VER)
|
||||
# define ATOMIC_INLINE static __forceinline
|
||||
#else
|
||||
# define ATOMIC_INLINE static inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define _ATOMIC_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define _ATOMIC_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define _ATOMIC_MAYBE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
# define _ATOMIC_LIKELY(x) (x)
|
||||
# define _ATOMIC_UNLIKELY(x) (x)
|
||||
# define _ATOMIC_MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
#if defined(__SIZEOF_POINTER__)
|
||||
# define LG_SIZEOF_PTR __SIZEOF_POINTER__
|
||||
#elif defined(UINTPTR_MAX)
|
||||
# if (UINTPTR_MAX == 0xFFFFFFFF)
|
||||
# define LG_SIZEOF_PTR 4
|
||||
# elif (UINTPTR_MAX == 0xFFFFFFFFFFFFFFFF)
|
||||
# define LG_SIZEOF_PTR 8
|
||||
# endif
|
||||
#elif defined(__WORDSIZE) /* Fallback for older glibc and cpp */
|
||||
# if (__WORDSIZE == 32)
|
||||
# define LG_SIZEOF_PTR 4
|
||||
# elif (__WORDSIZE == 64)
|
||||
# define LG_SIZEOF_PTR 8
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef LG_SIZEOF_PTR
|
||||
# error "Cannot find pointer size"
|
||||
#endif
|
||||
|
||||
#if (UINT_MAX == 0xFFFFFFFF)
|
||||
# define LG_SIZEOF_INT 4
|
||||
#elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF)
|
||||
# define LG_SIZEOF_INT 8
|
||||
#else
|
||||
# error "Cannot find int size"
|
||||
#endif
|
||||
|
||||
/* Copied from BLI_utils... */
|
||||
/* C++ can't use _Static_assert, expects static_assert() but c++0x only,
|
||||
* Coverity also errors out. */
|
||||
#if (!defined(__cplusplus)) && (!defined(__COVERITY__)) && (defined(__GNUC__)) /* GCC only. */
|
||||
# define ATOMIC_STATIC_ASSERT(a, msg) __extension__ _Static_assert(a, msg);
|
||||
#else
|
||||
/* Code adapted from http://www.pixelbeat.org/programming/gcc/static_assert.html */
|
||||
/* Note we need the two concats below because arguments to ## are not expanded, so we need to
|
||||
* expand __LINE__ with one indirection before doing the actual concatenation. */
|
||||
# define ATOMIC_ASSERT_CONCAT_(a, b) a##b
|
||||
# define ATOMIC_ASSERT_CONCAT(a, b) ATOMIC_ASSERT_CONCAT_(a, b)
|
||||
/* These can't be used after statements in c89. */
|
||||
# if defined(__COUNTER__) /* MSVC */
|
||||
# define ATOMIC_STATIC_ASSERT(a, msg) \
|
||||
; \
|
||||
enum { ATOMIC_ASSERT_CONCAT(static_assert_, __COUNTER__) = 1 / (int)(!!(a)) };
|
||||
# else /* older gcc, clang... */
|
||||
/* This can't be used twice on the same line so ensure if using in headers
|
||||
* that the headers are not included twice (by wrapping in #ifndef...#endif)
|
||||
* Note it doesn't cause an issue when used on same line of separate modules
|
||||
* compiled with gcc -combine -fwhole-program. */
|
||||
# define ATOMIC_STATIC_ASSERT(a, msg) \
|
||||
; \
|
||||
enum { ATOMIC_ASSERT_CONCAT(assert_line_, __LINE__) = 1 / (int)(!!(a)) };
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* __ATOMIC_OPS_UTILS_H__ */
|
||||
1104
blender-5.2.0/intern/atomic/tests/atomic_test.cc
Normal file
1104
blender-5.2.0/intern/atomic/tests/atomic_test.cc
Normal file
File diff suppressed because it is too large
Load Diff
206
blender-5.2.0/intern/clog/CLG_log.h
Normal file
206
blender-5.2.0/intern/clog/CLG_log.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup intern_clog
|
||||
*
|
||||
* C Logging Library (clog)
|
||||
* ========================
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
*
|
||||
* - `CLG_LOGREF_DECLARE_GLOBAL` macro to declare #CLG_LogRef pointers.
|
||||
* - `CLOG_` prefixed macros for logging.
|
||||
*
|
||||
* Identifiers
|
||||
* -----------
|
||||
*
|
||||
* #CLG_LogRef holds an identifier which defines the category of the logger.
|
||||
*
|
||||
* You can define and use identifiers as needed, logging will lazily initialize them.
|
||||
*
|
||||
* By convention lower case dot separated identifiers are used, eg:
|
||||
* `module.sub_module`, this allows filtering by `module.*`,
|
||||
* see #CLG_type_filter_include, #CLG_type_filter_exclude
|
||||
*
|
||||
* There is currently no functionality to remove a category once it's created.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define _CLOG_ATTR_NONNULL(args...) __attribute__((nonnull(args)))
|
||||
#else
|
||||
# define _CLOG_ATTR_NONNULL(...)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) \
|
||||
__attribute__((format(printf, format_param, dots_param)))
|
||||
#else
|
||||
# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_ARG(x) "" #x
|
||||
#define STRINGIFY_APPEND(a, b) "" a #b
|
||||
#define STRINGIFY(x) STRINGIFY_APPEND("", x)
|
||||
|
||||
struct CLogContext;
|
||||
|
||||
enum CLG_Level {
|
||||
/* Similar to assert. This logs the message, then a stack trace and abort. */
|
||||
CLG_LEVEL_FATAL = 0,
|
||||
/* An error we can recover from, should not happen. */
|
||||
CLG_LEVEL_ERROR = 1,
|
||||
/* General warnings (which aren't necessary to show to users). */
|
||||
CLG_LEVEL_WARN = 2,
|
||||
/* Information about devices, files, configuration, user operations. */
|
||||
CLG_LEVEL_INFO = 3,
|
||||
/* Debugging information for developers. */
|
||||
CLG_LEVEL_DEBUG = 4,
|
||||
/* Very verbose code execution tracing. */
|
||||
CLG_LEVEL_TRACE = 5,
|
||||
};
|
||||
#define CLG_LEVEL_LEN (CLG_LEVEL_TRACE + 1)
|
||||
|
||||
/* Each logger ID has one of these. */
|
||||
struct CLG_LogType {
|
||||
struct CLG_LogType *next;
|
||||
char identifier[64];
|
||||
/** FILE output. */
|
||||
struct CLogContext *ctx;
|
||||
/** Control behavior. */
|
||||
CLG_Level level;
|
||||
};
|
||||
|
||||
struct CLG_LogRef {
|
||||
CLG_LogRef(const char *identifier);
|
||||
|
||||
const char *identifier;
|
||||
CLG_LogType *type;
|
||||
struct CLG_LogRef *next;
|
||||
};
|
||||
|
||||
void CLG_log_str(const CLG_LogType *lg,
|
||||
enum CLG_Level level,
|
||||
const char *file_line,
|
||||
const char *fn,
|
||||
const char *message) _CLOG_ATTR_NONNULL(1, 3, 4, 5);
|
||||
void CLG_logf(const CLG_LogType *lg,
|
||||
enum CLG_Level level,
|
||||
const char *file_line,
|
||||
const char *fn,
|
||||
const char *format,
|
||||
...) _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
|
||||
void CLG_log_raw(const CLG_LogType *lg, const char *message);
|
||||
|
||||
/* Main initializer and destructor (per session, not logger). */
|
||||
void CLG_init();
|
||||
void CLG_exit();
|
||||
|
||||
void CLG_output_set(void *file_handle);
|
||||
void CLG_output_use_source_set(int value);
|
||||
void CLG_output_use_basename_set(int value);
|
||||
void CLG_output_use_timestamp_set(int value);
|
||||
void CLG_output_use_memory_set(int value);
|
||||
void CLG_error_fn_set(void (*error_fn)(void *file_handle));
|
||||
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
|
||||
void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle));
|
||||
|
||||
void CLG_type_filter_include(const char *type_match, int type_match_len);
|
||||
void CLG_type_filter_exclude(const char *type_match, int type_match_len);
|
||||
|
||||
void CLG_level_set(CLG_Level level);
|
||||
|
||||
void CLG_logref_init(CLG_LogRef *clg_ref);
|
||||
|
||||
void CLG_logref_register(CLG_LogRef *clg_ref);
|
||||
void CLG_logref_list_all(void (*callback)(const char *identifier, void *user_data),
|
||||
void *user_data);
|
||||
|
||||
int CLG_color_support_get(CLG_LogRef *clg_ref);
|
||||
|
||||
/* When true, quiet any NOCHECK logs that would otherwise be printed regardless of log filters
|
||||
* and levels. This is used so command line tools can control output without unnecessary noise.
|
||||
*
|
||||
* Note this does not silence log filters and levels that have been explicitly enabled. */
|
||||
void CLG_quiet_set(bool quiet);
|
||||
bool CLG_quiet_get();
|
||||
|
||||
inline CLG_LogRef::CLG_LogRef(const char *identifier)
|
||||
: identifier(identifier), type(nullptr), next(nullptr)
|
||||
{
|
||||
CLG_logref_register(this);
|
||||
}
|
||||
|
||||
/** Declare outside function, declare as extern in header. */
|
||||
#define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
|
||||
static CLG_LogRef _static_##var = {id}; \
|
||||
CLG_LogRef *var = &_static_##var
|
||||
|
||||
/** Initialize struct once. */
|
||||
#define CLOG_ENSURE(clg_ref) \
|
||||
((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
|
||||
|
||||
#define CLOG_CHECK(clg_ref, verbose_level, ...) \
|
||||
((void)CLOG_ENSURE(clg_ref), ((clg_ref)->type->level >= verbose_level))
|
||||
|
||||
#define CLOG_AT_LEVEL(clg_ref, verbose_level, ...) \
|
||||
{ \
|
||||
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (_lg_ty->level >= verbose_level) { \
|
||||
CLG_logf(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define CLOG_AT_LEVEL_NOCHECK(clg_ref, verbose_level, ...) \
|
||||
{ \
|
||||
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (!CLG_quiet_get() || _lg_ty->level >= verbose_level) { \
|
||||
CLG_logf(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define CLOG_STR_AT_LEVEL(clg_ref, verbose_level, str) \
|
||||
{ \
|
||||
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (_lg_ty->level >= verbose_level) { \
|
||||
CLG_log_str(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define CLOG_STR_AT_LEVEL_NOCHECK(clg_ref, verbose_level, str) \
|
||||
{ \
|
||||
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (!CLG_quiet_get() || _lg_ty->level >= verbose_level) { \
|
||||
CLG_log_str(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* Log with format string. */
|
||||
#define CLOG_FATAL(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_FATAL, __VA_ARGS__)
|
||||
#define CLOG_ERROR(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_ERROR, __VA_ARGS__)
|
||||
#define CLOG_WARN(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_WARN, __VA_ARGS__)
|
||||
#define CLOG_INFO(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_INFO, __VA_ARGS__)
|
||||
#define CLOG_DEBUG(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_DEBUG, __VA_ARGS__)
|
||||
#define CLOG_TRACE(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_TRACE, __VA_ARGS__)
|
||||
|
||||
/* Log single string. */
|
||||
#define CLOG_STR_FATAL(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_FATAL, str)
|
||||
#define CLOG_STR_ERROR(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_ERROR, str)
|
||||
#define CLOG_STR_WARN(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_WARN, str)
|
||||
#define CLOG_STR_INFO(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_INFO, str)
|
||||
#define CLOG_STR_DEBUG(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_DEBUG, str)
|
||||
#define CLOG_STR_TRACE(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_TRACE, str)
|
||||
|
||||
/* Log regardless of filters and levels, for a few important messages like blend save and load.
|
||||
* Only #CLG_quiet_set will silence these. */
|
||||
#define CLOG_INFO_NOCHECK(clg_ref, format, ...) \
|
||||
CLOG_AT_LEVEL_NOCHECK(clg_ref, CLG_LEVEL_INFO, format, __VA_ARGS__)
|
||||
#define CLOG_STR_INFO_NOCHECK(clg_ref, str) CLOG_STR_AT_LEVEL_NOCHECK(clg_ref, CLG_LEVEL_INFO, str)
|
||||
28
blender-5.2.0/intern/clog/CMakeLists.txt
Normal file
28
blender-5.2.0/intern/clog/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: 2002-2022 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC
|
||||
clog.cc
|
||||
|
||||
CLG_log.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::intern::atomic
|
||||
)
|
||||
|
||||
# Disabled for `makesdna` & `makesrna`.
|
||||
add_definitions(-DWITH_CLOG_PTHREADS)
|
||||
|
||||
blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::intern::clog ALIAS bf_intern_clog)
|
||||
1016
blender-5.2.0/intern/clog/clog.cc
Normal file
1016
blender-5.2.0/intern/clog/clog.cc
Normal file
File diff suppressed because it is too large
Load Diff
413
blender-5.2.0/intern/cycles/CMakeLists.txt
Normal file
413
blender-5.2.0/intern/cycles/CMakeLists.txt
Normal file
@@ -0,0 +1,413 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Standalone or with Blender
|
||||
if(NOT WITH_BLENDER)
|
||||
set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX})
|
||||
else()
|
||||
set(WITH_CYCLES_BLENDER ON)
|
||||
# WINDOWS_PYTHON_DEBUG needs to write into the user addons folder since it will
|
||||
# be started with --env-system-scripts pointing to the release folder, which will
|
||||
# lack the cycles addon, and we don't want to write into it.
|
||||
if(NOT WINDOWS_PYTHON_DEBUG)
|
||||
set(CYCLES_INSTALL_PATH "scripts/addons_core/cycles")
|
||||
else()
|
||||
set(CYCLES_INSTALL_PATH
|
||||
"$ENV{appdata}/blender foundation/blender/${BLENDER_VERSION}/scripts/addons_core/cycles"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# External Libraries
|
||||
|
||||
if(NOT CYCLES_STANDALONE_REPOSITORY)
|
||||
include(cmake/external_libs.cmake)
|
||||
include(cmake/macros.cmake)
|
||||
endif()
|
||||
|
||||
# Build Flags
|
||||
# TODO: this code could be refactored a bit to avoid duplication.
|
||||
# NOTE: CXX_HAS_SSE42 is needed in case passing SSE flags fails altogether (`gcc-arm`).
|
||||
|
||||
# Determine vectorization support and required build flags
|
||||
if(WITH_CYCLES_NATIVE_ONLY)
|
||||
set(CXX_HAS_SSE42 FALSE)
|
||||
set(CXX_HAS_AVX2 FALSE)
|
||||
set(CXX_HAS_F16C FALSE)
|
||||
add_definitions(
|
||||
-DWITH_KERNEL_NATIVE
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
add_check_cxx_compiler_flags(
|
||||
CMAKE_CXX_FLAGS
|
||||
_has_march_native "-march=native"
|
||||
)
|
||||
if(_has_march_native)
|
||||
string(APPEND CMAKE_CXX_FLAGS " -march=native")
|
||||
else()
|
||||
string(APPEND CMAKE_CXX_FLAGS "")
|
||||
endif()
|
||||
unset(_has_march_native)
|
||||
else()
|
||||
if(NOT MSVC_NATIVE_ARCH_FLAGS)
|
||||
try_run(
|
||||
arch_run_result
|
||||
arch_compile_result
|
||||
${CMAKE_CURRENT_BINARY_DIR}/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/msvc_arch_flags.c
|
||||
COMPILE_OUTPUT_VARIABLE arch_compile_output
|
||||
RUN_OUTPUT_VARIABLE arch_run_output
|
||||
)
|
||||
if(arch_compile_result AND "${arch_run_result}" EQUAL "0")
|
||||
string(STRIP ${arch_run_output} arch_run_output)
|
||||
set(MSVC_NATIVE_ARCH_FLAGS ${arch_run_output} CACHE STRING "MSVC Native architecture flags")
|
||||
endif()
|
||||
endif()
|
||||
string(APPEND CMAKE_CXX_FLAGS " ${MSVC_NATIVE_ARCH_FLAGS}")
|
||||
endif()
|
||||
elseif(SUPPORTS_NEON_BUILD AND SSE2NEON_FOUND)
|
||||
# On ARM we use SSE2NEON to compile the cycles SSE kernels into native NEON instructions
|
||||
# Disable the CXX_HAS_* flags so we don't add any SSE or AVX compiler flags.
|
||||
set(CXX_HAS_SSE42 FALSE)
|
||||
set(CXX_HAS_AVX2 FALSE)
|
||||
set(CXX_HAS_F16C FALSE)
|
||||
elseif(WIN32 AND MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CXX_HAS_SSE42 TRUE)
|
||||
set(CXX_HAS_AVX2 TRUE)
|
||||
set(CXX_HAS_F16C TRUE)
|
||||
|
||||
# No separate f16c flag for MSVC.
|
||||
set(CYCLES_AVX2_FLAGS "/arch:AVX2")
|
||||
set(CYCLES_AVX2_F16C_FLAGS "${CYCLES_AVX2_FLAGS}")
|
||||
|
||||
# there is no /arch:SSE3, but intrinsics are available anyway
|
||||
if(CMAKE_CL_64)
|
||||
set(CYCLES_SSE42_FLAGS "")
|
||||
else()
|
||||
set(CYCLES_SSE42_FLAGS "/arch:SSE2")
|
||||
endif()
|
||||
elseif((CMAKE_C_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
check_cxx_compiler_flag(-msse4.2 CXX_HAS_SSE42)
|
||||
check_cxx_compiler_flag(-mavx2 CXX_HAS_AVX2)
|
||||
check_cxx_compiler_flag(-mf16c CXX_HAS_F16C)
|
||||
|
||||
if(CXX_HAS_SSE42)
|
||||
set(CYCLES_SSE42_FLAGS "-msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2")
|
||||
if(CXX_HAS_AVX2 AND CXX_HAS_F16C)
|
||||
set(CYCLES_AVX2_FLAGS "${CYCLES_SSE42_FLAGS} -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2 -mf16c")
|
||||
set(CYCLES_AVX2_F16C_FLAGS "${CYCLES_AVX2_FLAGS} -mf16c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
check_cxx_compiler_flag(/QxSSE4.2 CXX_HAS_SSE42)
|
||||
check_cxx_compiler_flag(/QxCORE-AVX2 CXX_HAS_AVX2)
|
||||
set(CXX_HAS_F16C ${CXX_HAS_AVX2}) # Same flag enables AVX, FMA and F16C
|
||||
|
||||
if(CXX_HAS_SSE42)
|
||||
set(CYCLES_SSE42_FLAGS "/QxSSE4.2")
|
||||
|
||||
if(CXX_HAS_AVX2 AND CXX_HAS_F16C)
|
||||
set(CYCLES_AVX2_FLAGS "/QxCORE-AVX2")
|
||||
set(CYCLES_AVX2_F16C_FLAGS "${CYCLES_AVX2_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
check_cxx_compiler_flag(-xsse4.2 CXX_HAS_SSE42)
|
||||
check_cxx_compiler_flag(-xcore-avx2 CXX_HAS_AVX2)
|
||||
set(CXX_HAS_F16C ${CXX_HAS_AVX2}) # Same flag enables AVX, FMA and F16C
|
||||
|
||||
if(CXX_HAS_SSE42)
|
||||
set(CYCLES_SSE42_FLAGS "-xsse4.2")
|
||||
|
||||
if(CXX_HAS_AVX2 AND CXX_HAS_F16C)
|
||||
set(CYCLES_AVX2_FLAGS "-xcore-avx2")
|
||||
set(CYCLES_AVX2_F16C_FLAGS "${CYCLES_AVX2_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CXX_HAS_SSE42)
|
||||
add_definitions(
|
||||
-DWITH_KERNEL_SSE42
|
||||
)
|
||||
# We require SSE4.2 as a minimum, so make use of it
|
||||
string(APPEND CMAKE_CXX_FLAGS " ${CYCLES_SSE42_FLAGS}")
|
||||
endif()
|
||||
|
||||
if(CXX_HAS_AVX2)
|
||||
add_definitions(-DWITH_KERNEL_AVX2)
|
||||
endif()
|
||||
|
||||
# Enable math optimizations
|
||||
|
||||
if(WIN32 AND MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# Unlike GCC/clang we still use fast math, because there is no fine
|
||||
# grained control and the speedup we get here is too big to ignore.
|
||||
string(APPEND CMAKE_CXX_FLAGS " /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELEASE " /Ox")
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " /Ox")
|
||||
string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " /Ox")
|
||||
|
||||
# `jumptablerdata` improves performance when there is contention in large switch statements
|
||||
# such as in `svm.h`.
|
||||
# This flag is supported starting with MSVC 17.7 preview 3:
|
||||
# https://learn.microsoft.com/en-us/cpp/build/reference/jump-table-rdata
|
||||
# This is an x64 specific optimization.
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.37.32820
|
||||
AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
|
||||
string(APPEND CMAKE_CXX_FLAGS " /jumptablerdata")
|
||||
endif()
|
||||
elseif((CMAKE_C_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
# Assume no signal trapping for better code generation.
|
||||
list(APPEND CYCLES_MATH_FLAGS "-fno-trapping-math")
|
||||
# Avoid overhead of setting `errno` for NaNs.
|
||||
list(APPEND CYCLES_MATH_FLAGS "-fno-math-errno")
|
||||
# Let compiler optimize 0.0 - x without worrying about signed zeros.
|
||||
list(APPEND CYCLES_MATH_FLAGS "-fno-signed-zeros")
|
||||
# Disable FMA auto-vectorization across different statements,
|
||||
# which creates havoc in "intern/cycles/blender/camera.cpp"
|
||||
# with GCC-15.2.1, for something like:
|
||||
# `const float2 dv = 2.0f * (aspectratio * bcam->shift + bcam->offset * aspect * 2.0f);`
|
||||
# (probably due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106902).
|
||||
list(APPEND CYCLES_MATH_FLAGS "-ffp-contract=on")
|
||||
# Let the compiler replace x/y with x*(1/y)
|
||||
list(APPEND CYCLES_MATH_FLAGS "-freciprocal-math")
|
||||
# Let the compiler reorder terms to save operations
|
||||
# NOTE: Disabled for now due to problems with bsdf_D for GGX on Linux (#130389)
|
||||
# list(APPEND CYCLES_MATH_FLAGS "-fassociative-math")
|
||||
# Don't enable `-ffinite-math-only` since the BVH code relies on NaNs.
|
||||
# Otherwise, we could just use `-ffast-math`.
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
# Assume no signal trapping for better code generation.
|
||||
list(APPEND CYCLES_MATH_FLAGS "-fno-signaling-nans")
|
||||
# Assume a fixed rounding mode for better constant folding.
|
||||
list(APPEND CYCLES_MATH_FLAGS "-fno-rounding-math")
|
||||
|
||||
if(CXX_HAS_SSE42)
|
||||
list(APPEND CYCLES_MATH_FLAGS "-mfpmath=sse")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32 AND MSVC)
|
||||
# Pass clang flags directly to clang otherwise. Clang-cl doesn't recognize
|
||||
# these flags by default
|
||||
list(TRANSFORM CYCLES_MATH_FLAGS PREPEND "/clang:")
|
||||
endif()
|
||||
|
||||
list(JOIN CYCLES_MATH_FLAGS " " CYCLES_MATH_FLAGS)
|
||||
string(APPEND CMAKE_CXX_FLAGS " ${CYCLES_MATH_FLAGS}")
|
||||
endif()
|
||||
|
||||
# Definitions and Includes
|
||||
|
||||
add_definitions(
|
||||
-DCCL_NAMESPACE_BEGIN=namespace\ ccl\ {
|
||||
-DCCL_NAMESPACE_END=}
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_DEBUG)
|
||||
add_definitions(-DWITH_CYCLES_DEBUG)
|
||||
endif()
|
||||
if(WITH_CYCLES_STANDALONE_GUI)
|
||||
add_definitions(-DWITH_CYCLES_STANDALONE_GUI)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_PTEX)
|
||||
add_definitions(-DWITH_PTEX)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
add_definitions(-DWITH_OSL)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX)
|
||||
add_definitions(-DWITH_CUDA)
|
||||
|
||||
if(WITH_CUDA_DYNLOAD)
|
||||
include_directories(
|
||||
../../extern/cuew/include
|
||||
)
|
||||
add_definitions(-DWITH_CUDA_DYNLOAD)
|
||||
else()
|
||||
include_directories(
|
||||
SYSTEM
|
||||
${CUDA_TOOLKIT_INCLUDE}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEVICE_HIP)
|
||||
add_definitions(-DWITH_HIP)
|
||||
|
||||
if(WITH_CYCLES_DEVICE_HIPRT)
|
||||
include_directories(
|
||||
${HIPRT_INCLUDE_DIRS}
|
||||
)
|
||||
add_definitions(-DWITH_HIPRT)
|
||||
endif()
|
||||
|
||||
if(WITH_HIP_DYNLOAD)
|
||||
include_directories(
|
||||
../../extern/hipew/include
|
||||
)
|
||||
add_definitions(-DWITH_HIP_DYNLOAD)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEVICE_OPTIX)
|
||||
find_package(OptiX 8.0.0)
|
||||
|
||||
if(OPTIX_FOUND)
|
||||
add_definitions(-DWITH_OPTIX)
|
||||
include_directories(
|
||||
SYSTEM
|
||||
${OPTIX_INCLUDE_DIR}
|
||||
)
|
||||
else()
|
||||
set_and_warn_library_found("OptiX" OPTIX_FOUND WITH_CYCLES_DEVICE_OPTIX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEVICE_METAL)
|
||||
add_definitions(-DWITH_METAL)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEVICE_ONEAPI)
|
||||
add_definitions(-DWITH_ONEAPI)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_EMBREE)
|
||||
add_definitions(-DWITH_EMBREE)
|
||||
if(WITH_CYCLES_DEVICE_ONEAPI AND EMBREE_SYCL_SUPPORT)
|
||||
# NOTE: The debug version of Embree is built without SYCL support on Windows
|
||||
# since 7fb480095e371f8f5ac4f647f0ba2fd78da486f7. This is not reflected in
|
||||
# EMBREE_SYCL_SUPPORT which is coming from Embree headers that aren't
|
||||
# differentiated for release and debug, so we handle this case here by
|
||||
# disabling its use when embree4_sycl_d.lib doesn't exist.
|
||||
set(EMBREE_SYCL_DEBUG_LIBRARY ${EMBREE_LIBRARIES})
|
||||
list(FILTER EMBREE_SYCL_DEBUG_LIBRARY INCLUDE REGEX "_sycl_d\\.lib$")
|
||||
if(WIN32 AND NOT EMBREE_SYCL_DEBUG_LIBRARY)
|
||||
add_compile_definitions("$<$<CONFIG:Release>:WITH_EMBREE_GPU>")
|
||||
add_compile_definitions("$<$<CONFIG:RelWithDebInfo>:WITH_EMBREE_GPU>")
|
||||
add_compile_definitions("$<$<CONFIG:MinSizeRel>:WITH_EMBREE_GPU>")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
|
||||
message(STATUS
|
||||
"The use of Embree GPU is disabled for the Debug configuration "
|
||||
"as embree${EMBREE_MAJOR_VERSION}_sycl_d.lib is not found."
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
add_definitions(-DWITH_EMBREE_GPU)
|
||||
endif()
|
||||
endif()
|
||||
add_definitions(-DEMBREE_MAJOR_VERSION=${EMBREE_MAJOR_VERSION})
|
||||
endif()
|
||||
|
||||
if(WITH_OPENIMAGEDENOISE)
|
||||
add_definitions(-DWITH_OPENIMAGEDENOISE)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
add_definitions(-DWITH_OPENVDB)
|
||||
endif()
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
add_definitions(-DWITH_NANOVDB)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENSUBDIV)
|
||||
add_definitions(-DWITH_OPENSUBDIV)
|
||||
endif()
|
||||
|
||||
add_definitions(-DWITH_OCIO)
|
||||
|
||||
if(WITH_CYCLES_PATH_GUIDING)
|
||||
add_definitions(-DWITH_PATH_GUIDING)
|
||||
|
||||
# The level of the guiding integration.
|
||||
# Different levels can be selected to measure the overhead of different stages.
|
||||
# 1 = recording the path segments
|
||||
# 2 = 1 + generating (not storing) sample data from the segments
|
||||
# 3 = 2 + storing the generated sample data
|
||||
# 4 = 3 + training the guiding fields
|
||||
# 5 = 4 + querying the trained guiding for sampling (full path guiding)
|
||||
add_definitions(-DPATH_GUIDING_LEVEL=5)
|
||||
endif()
|
||||
|
||||
# NaN debugging
|
||||
if(WITH_CYCLES_DEBUG_NAN)
|
||||
add_definitions(-DWITH_CYCLES_DEBUG_NAN)
|
||||
endif()
|
||||
|
||||
if(WITH_PUGIXML OR OPENIMAGEIO_PUGIXML_FOUND)
|
||||
add_definitions(-DWITH_PUGIXML)
|
||||
if((NOT OPENIMAGEIO_PUGIXML_FOUND) OR WIN32)
|
||||
add_definitions(-DWITH_SYSTEM_PUGIXML)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CYCLES_STANDALONE_REPOSITORY)
|
||||
include_directories(../third_party/atomic)
|
||||
else()
|
||||
include_directories(../atomic)
|
||||
endif()
|
||||
|
||||
# Warnings
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
add_check_cxx_compiler_flags(
|
||||
CMAKE_CXX_FLAGS
|
||||
_has_no_error_unused_macros "-Wno-error=unused-macros"
|
||||
)
|
||||
unset(_has_no_error_unused_macros)
|
||||
endif()
|
||||
|
||||
if(WITH_USD)
|
||||
add_definitions(-DWITH_USD)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE AND (NOT WITH_USD))
|
||||
set_and_warn_library_found("USD" WITH_USD WITH_CYCLES_HYDRA_RENDER_DELEGATE)
|
||||
endif()
|
||||
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE AND (NOT WITH_BLENDER) AND (NOT WITH_CYCLES_STANDALONE))
|
||||
set(CYCLES_INSTALL_PATH ${CYCLES_INSTALL_PATH}/hdCycles/resources)
|
||||
endif()
|
||||
|
||||
# Subdirectories
|
||||
|
||||
if(WITH_CYCLES_BLENDER)
|
||||
add_definitions(-DWITH_BLENDER_GUARDEDALLOC)
|
||||
add_subdirectory(blender)
|
||||
endif()
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(bvh)
|
||||
add_subdirectory(device)
|
||||
add_subdirectory(doc)
|
||||
add_subdirectory(graph)
|
||||
add_subdirectory(integrator)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(scene)
|
||||
add_subdirectory(session)
|
||||
add_subdirectory(subd)
|
||||
add_subdirectory(util)
|
||||
|
||||
# TODO(sergey): Make this to work with standalone repository.
|
||||
if(WITH_GTESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE OR (WITH_CYCLES_STANDALONE AND WITH_USD))
|
||||
add_subdirectory(hydra)
|
||||
endif()
|
||||
|
||||
if(NOT WITH_BLENDER)
|
||||
if(CYCLES_STANDALONE_REPOSITORY)
|
||||
delayed_do_install()
|
||||
else()
|
||||
delayed_do_install(${CMAKE_BINARY_DIR}/bin)
|
||||
endif()
|
||||
endif()
|
||||
174
blender-5.2.0/intern/cycles/app/CMakeLists.txt
Normal file
174
blender-5.2.0/intern/cycles/app/CMakeLists.txt
Normal file
@@ -0,0 +1,174 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#####################################################################
|
||||
# Cycles standalone executable
|
||||
#####################################################################
|
||||
|
||||
set(INC
|
||||
..
|
||||
)
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(LIB
|
||||
cycles_device
|
||||
cycles_kernel
|
||||
cycles_scene
|
||||
cycles_session
|
||||
cycles_bvh
|
||||
cycles_subd
|
||||
cycles_graph
|
||||
cycles_util
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
list(APPEND LIB cycles_kernel_osl)
|
||||
endif()
|
||||
|
||||
if(CYCLES_STANDALONE_REPOSITORY)
|
||||
list(APPEND LIB extern_sky)
|
||||
else()
|
||||
list(APPEND LIB bf_intern_sky)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
|
||||
list(APPEND LIB
|
||||
bf::dependencies::epoxy
|
||||
bf::dependencies::optional::sdl
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_USD)
|
||||
# Silence warning from USD headers using deprecated TBB header.
|
||||
add_definitions(
|
||||
-D__TBB_show_deprecation_message_atomic_H
|
||||
-D__TBB_show_deprecation_message_task_H
|
||||
)
|
||||
list(APPEND LIB
|
||||
cycles_hydra
|
||||
bf::dependencies::optional::usd
|
||||
)
|
||||
endif()
|
||||
|
||||
cycles_external_libraries_append(LIB)
|
||||
|
||||
# Common configuration.
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
if(APPLE AND NOT CYCLES_STANDALONE_REPOSITORY)
|
||||
set(app_output_dir Blender.app/Contents/MacOS)
|
||||
set(app_install_dir Blender.app/Contents/MacOS)
|
||||
else()
|
||||
set(app_install_dir ${CMAKE_INSTALL_PREFIX})
|
||||
endif()
|
||||
|
||||
# Application build targets
|
||||
|
||||
if(WITH_CYCLES_STANDALONE)
|
||||
set(SRC
|
||||
cycles_standalone.cpp
|
||||
cycles_xml.cpp
|
||||
cycles_xml.h
|
||||
oiio_output_driver.cpp
|
||||
oiio_output_driver.h
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_STANDALONE_GUI)
|
||||
list(APPEND SRC
|
||||
opengl/display_driver.cpp
|
||||
opengl/display_driver.h
|
||||
opengl/shader.cpp
|
||||
opengl/shader.h
|
||||
opengl/window.cpp
|
||||
opengl/window.h
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(cycles ${SRC} ${INC} ${INC_SYS})
|
||||
unset(SRC)
|
||||
|
||||
target_link_libraries(cycles PRIVATE ${LIB})
|
||||
|
||||
if(APPLE)
|
||||
if(WITH_CYCLES_STANDALONE_GUI)
|
||||
# Frameworks used by SDL.
|
||||
string(CONCAT _cycles_sdl_frameworks
|
||||
" -framework AudioToolbox"
|
||||
" -framework AudioUnit"
|
||||
" -framework Cocoa"
|
||||
" -framework CoreAudio"
|
||||
" -framework CoreHaptics"
|
||||
" -framework CoreVideo"
|
||||
" -framework ForceFeedback"
|
||||
" -framework GameController"
|
||||
)
|
||||
set_property(
|
||||
TARGET cycles
|
||||
APPEND PROPERTY LINK_FLAGS
|
||||
"${_cycles_sdl_frameworks}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CYCLES_STANDALONE_REPOSITORY)
|
||||
cycles_install_libraries(cycles)
|
||||
|
||||
if(WITH_USD AND USD_LIBRARY_DIR)
|
||||
install(DIRECTORY
|
||||
${USD_LIBRARY_DIR}/usd
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
)
|
||||
|
||||
install(DIRECTORY
|
||||
${USD_LIBRARY_DIR}/../plugin/usd
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS cycles
|
||||
DESTINATION ${app_install_dir})
|
||||
|
||||
if(DEFINED app_output_dir)
|
||||
set_target_properties(cycles
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${app_output_dir}")
|
||||
endif()
|
||||
|
||||
add_test(
|
||||
NAME cycles_version
|
||||
COMMAND $<TARGET_FILE:cycles> --version)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_PRECOMPUTE)
|
||||
set(SRC
|
||||
cycles_precompute.cpp
|
||||
)
|
||||
|
||||
add_executable(cycles_precompute ${SRC} ${INC} ${INC_SYS})
|
||||
unset(SRC)
|
||||
|
||||
target_link_libraries(cycles_precompute
|
||||
PRIVATE cycles_util
|
||||
PRIVATE bf::dependencies::openimageio
|
||||
PRIVATE bf::dependencies::optional::tbb
|
||||
PRIVATE bf::dependencies::optional::osl)
|
||||
|
||||
if(NOT CYCLES_STANDALONE_REPOSITORY)
|
||||
target_link_libraries(cycles_precompute
|
||||
PRIVATE bf::intern::guardedalloc)
|
||||
endif()
|
||||
|
||||
if(DEFINED app_output_dir)
|
||||
set_target_properties(cycles_precompute
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${app_output_dir}")
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS cycles_precompute
|
||||
DESTINATION ${app_install_dir})
|
||||
endif()
|
||||
296
blender-5.2.0/intern/cycles/app/cycles_precompute.cpp
Normal file
296
blender-5.2.0/intern/cycles/app/cycles_precompute.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/tbb.h"
|
||||
|
||||
#include "kernel/closure/bsdf_microfacet.h"
|
||||
#include "kernel/sample/sobol_burley.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static float precompute_ggx_E(const float rough, const float mu, const float3 rand)
|
||||
{
|
||||
MicrofacetBsdf bsdf;
|
||||
bsdf.weight = one_float3();
|
||||
bsdf.sample_weight = 1.0f;
|
||||
bsdf.N = make_float3(0.0f, 0.0f, 1.0f);
|
||||
bsdf.alpha_x = bsdf.alpha_y = sqr(rough);
|
||||
bsdf.ior = 1.0f;
|
||||
bsdf.T = make_float3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
bsdf_microfacet_ggx_setup(&bsdf);
|
||||
|
||||
float3 omega_in;
|
||||
Spectrum eval;
|
||||
float pdf = 0.0f;
|
||||
float sampled_eta;
|
||||
float2 sampled_roughness;
|
||||
bsdf_microfacet_ggx_sample(nullptr,
|
||||
(ShaderClosure *)&bsdf,
|
||||
make_float3(0.0f, 0.0f, 1.0f),
|
||||
make_float3(sqrtf(1.0f - sqr(mu)), 0.0f, mu),
|
||||
rand,
|
||||
&eval,
|
||||
&omega_in,
|
||||
&pdf,
|
||||
&sampled_roughness,
|
||||
&sampled_eta);
|
||||
if (pdf != 0.0f) {
|
||||
return average(eval) / pdf;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static float precompute_ggx_glass_E(const float rough,
|
||||
const float mu,
|
||||
const float eta,
|
||||
const float3 rand)
|
||||
{
|
||||
MicrofacetBsdf bsdf;
|
||||
bsdf.weight = one_float3();
|
||||
bsdf.sample_weight = 1.0f;
|
||||
bsdf.N = make_float3(0.0f, 0.0f, 1.0f);
|
||||
bsdf.alpha_x = bsdf.alpha_y = sqr(rough);
|
||||
bsdf.ior = eta;
|
||||
bsdf.T = make_float3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
bsdf_microfacet_ggx_glass_setup(&bsdf);
|
||||
|
||||
float3 omega_in;
|
||||
Spectrum eval;
|
||||
float pdf = 0.0f;
|
||||
float sampled_eta;
|
||||
float2 sampled_roughness;
|
||||
bsdf_microfacet_ggx_sample(nullptr,
|
||||
(ShaderClosure *)&bsdf,
|
||||
make_float3(0.0f, 0.0f, 1.0f),
|
||||
make_float3(sqrtf(1.0f - sqr(mu)), 0.0f, mu),
|
||||
rand,
|
||||
&eval,
|
||||
&omega_in,
|
||||
&pdf,
|
||||
&sampled_roughness,
|
||||
&sampled_eta);
|
||||
if (pdf != 0.0f) {
|
||||
return average(eval) / pdf;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static float precompute_ggx_gen_schlick_s(
|
||||
const float rough, const float mu, const float eta, const float exponent, const float3 rand)
|
||||
{
|
||||
MicrofacetBsdf bsdf;
|
||||
bsdf.weight = one_float3();
|
||||
bsdf.sample_weight = 1.0f;
|
||||
bsdf.N = make_float3(0.0f, 0.0f, 1.0f);
|
||||
bsdf.alpha_x = bsdf.alpha_y = sqr(rough);
|
||||
bsdf.ior = eta;
|
||||
bsdf.T = make_float3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
bsdf_microfacet_ggx_setup(&bsdf);
|
||||
|
||||
FresnelGeneralizedSchlick fresnel;
|
||||
fresnel.reflection_tint = one_float3();
|
||||
fresnel.transmission_tint = one_float3();
|
||||
fresnel.f0 = make_float3(0.0f, 1.0f, 0.0f);
|
||||
fresnel.f90 = make_float3(1.0f, 1.0f, 0.0f);
|
||||
fresnel.exponent = exponent;
|
||||
|
||||
bsdf.fresnel_type = MicrofacetFresnel::GENERALIZED_SCHLICK;
|
||||
bsdf.fresnel = &fresnel;
|
||||
|
||||
float3 omega_in;
|
||||
Spectrum eval;
|
||||
float pdf = 0.0f;
|
||||
float sampled_eta;
|
||||
float2 sampled_roughness;
|
||||
bsdf_microfacet_ggx_sample(nullptr,
|
||||
(ShaderClosure *)&bsdf,
|
||||
make_float3(0.0f, 0.0f, 1.0f),
|
||||
make_float3(sqrtf(1.0f - sqr(mu)), 0.0f, mu),
|
||||
rand,
|
||||
&eval,
|
||||
&omega_in,
|
||||
&pdf,
|
||||
&sampled_roughness,
|
||||
&sampled_eta);
|
||||
if (pdf != 0.0f) {
|
||||
/* The idea here is that the resulting Fresnel factor is always bounded by
|
||||
* F0..F90, so it's enough to precompute and store the interpolation factor. */
|
||||
return saturatef(eval.x / eval.y);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
inline float ior_parametrization(const float z)
|
||||
{
|
||||
/* This parametrization ensures that the entire [1..inf] range of IORs is covered
|
||||
* and that most precision is allocated to the common areas (1-2). */
|
||||
return ior_from_F0(sqr(sqr(z)));
|
||||
}
|
||||
|
||||
struct PrecomputeTerm {
|
||||
int samples;
|
||||
int nx, ny, nz;
|
||||
std::function<float(float, float, float, float3)> evaluation;
|
||||
};
|
||||
|
||||
static bool cycles_precompute(std::string name)
|
||||
{
|
||||
std::map<string, PrecomputeTerm> precompute_terms;
|
||||
/* Overall albedo of the GGX microfacet BRDF, depending on cosI and roughness. */
|
||||
precompute_terms["ggx_E"] = {
|
||||
1 << 23, 32, 32, 1, [](const float rough, const float mu, float, const float3 rand) {
|
||||
return precompute_ggx_E(rough, mu, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BRDF, averaged over cosI */
|
||||
precompute_terms["ggx_Eavg"] = {
|
||||
1 << 26, 32, 1, 1, [](const float rough, const float mu, float, const float3 rand) {
|
||||
return 2.0f * mu * precompute_ggx_E(rough, mu, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* depending on cosI and roughness, for IOR>1. */
|
||||
precompute_terms["ggx_glass_E"] = {
|
||||
1 << 23,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return precompute_ggx_glass_E(rough, mu, ior, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* averaged over cosI, for IOR>1. */
|
||||
precompute_terms["ggx_glass_Eavg"] = {
|
||||
1 << 26, 16, 1, 16, [](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return 2.0f * mu * precompute_ggx_glass_E(rough, mu, ior, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* depending on cosI and roughness, for IOR<1. */
|
||||
precompute_terms["ggx_glass_inv_E"] = {
|
||||
1 << 23,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return precompute_ggx_glass_E(rough, mu, 1.0f / ior, rand);
|
||||
}};
|
||||
/* Overall albedo of the GGX microfacet BSDF with dielectric Fresnel,
|
||||
* averaged over cosI, for IOR<1. */
|
||||
precompute_terms["ggx_glass_inv_Eavg"] = {
|
||||
1 << 26, 16, 1, 16, [](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return 2.0f * mu * precompute_ggx_glass_E(rough, mu, 1.0f / ior, rand);
|
||||
}};
|
||||
|
||||
/* Interpolation factor between F0 and F90 for the generalized Schlick Fresnel,
|
||||
* depending on cosI and roughness, for IOR>1, using dielectric Fresnel mode. */
|
||||
precompute_terms["ggx_gen_schlick_ior_s"] = {
|
||||
1 << 20,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
const float ior = ior_parametrization(z);
|
||||
return precompute_ggx_gen_schlick_s(rough, mu, ior, -1.0f, rand);
|
||||
}};
|
||||
|
||||
/* Interpolation factor between F0 and F90 for the generalized Schlick Fresnel,
|
||||
* depending on cosI and roughness, for IOR>1. */
|
||||
precompute_terms["ggx_gen_schlick_s"] = {
|
||||
1 << 20,
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
[](const float rough, const float mu, const float z, const float3 rand) {
|
||||
/* Remap 0..1 to 0..inf, with 0.5 mapping to 5 (the default value). */
|
||||
const float exponent = 5.0f * ((1.0f - z) / z);
|
||||
return precompute_ggx_gen_schlick_s(rough, mu, 1.0f, exponent, rand);
|
||||
}};
|
||||
|
||||
if (!precompute_terms.contains(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PrecomputeTerm &term = precompute_terms[name];
|
||||
|
||||
const int samples = term.samples;
|
||||
const int nz = term.nz;
|
||||
const int ny = term.ny;
|
||||
const int nx = term.nx;
|
||||
|
||||
std::cout << "static const float table_" << name << "[" << nz * ny * nx << "] = {" << std::endl;
|
||||
for (int z = 0; z < nz; z++) {
|
||||
array<float> data(nx * ny);
|
||||
parallel_for(0, nx * ny, [&](int64_t i) {
|
||||
const int y = i / nx;
|
||||
const int x = i % nx;
|
||||
const uint seed = hash_uint2(x, y);
|
||||
double sum = 0.0;
|
||||
for (int sample = 0; sample < samples; sample++) {
|
||||
const float4 rand = sobol_burley_sample_4D(sample, 0, seed, 0xffffffff);
|
||||
|
||||
const float rough = (nx == 1) ? 0.0f : clamp(float(x) / float(nx - 1), 1e-4f, 1.0f);
|
||||
const float mu = (ny == 1) ? rand.w : clamp(float(y) / float(ny - 1), 1e-4f, 1.0f);
|
||||
const float ior = (nz == 1) ? 0.0f : clamp(float(z) / float(nz - 1), 1e-4f, 0.99f);
|
||||
|
||||
float value = term.evaluation(rough, mu, ior, make_float3(rand));
|
||||
if (isnan(value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
sum += (double)value;
|
||||
}
|
||||
data[y * nx + x] = saturatef(float(sum / double(samples)));
|
||||
});
|
||||
|
||||
/* Print data formatted as C++ array */
|
||||
for (int y = 0; y < ny; y++) {
|
||||
std::cout << " ";
|
||||
for (int x = 0; x < nx; x++) {
|
||||
std::cout << std::to_string(data[y * nx + x]);
|
||||
if (x + 1 < nx) {
|
||||
/* Next number will follow in same line */
|
||||
std::cout << "f, ";
|
||||
}
|
||||
else if (y + 1 < ny || z + 1 < nz) {
|
||||
/* Next number will follow in next line */
|
||||
std::cout << "f,";
|
||||
}
|
||||
else {
|
||||
/* No next number */
|
||||
std::cout << "f";
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
/* If the array is three-dimensional, put an empty line between each slice. */
|
||||
if (ny > 1 && z + 1 < nz) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "};" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
int main(const int argc, const char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return 1;
|
||||
}
|
||||
return ccl::cycles_precompute(argv[1]) ? 0 : 1;
|
||||
}
|
||||
573
blender-5.2.0/intern/cycles/app/cycles_standalone.cpp
Normal file
573
blender-5.2.0/intern/cycles/app/cycles_standalone.cpp
Normal file
@@ -0,0 +1,573 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "device/device.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/integrator.h"
|
||||
#include "scene/scene.h"
|
||||
#include "session/buffers.h"
|
||||
#include "session/session.h"
|
||||
|
||||
#include "util/args.h"
|
||||
#include "util/log.h"
|
||||
#include "util/path.h"
|
||||
#include "util/progress.h"
|
||||
#include "util/string.h"
|
||||
#include "util/system.h"
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
# include "util/time.h"
|
||||
# include "util/transform.h"
|
||||
#endif
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/version.h"
|
||||
|
||||
#ifdef WITH_USD
|
||||
# include "hydra/file_reader.h"
|
||||
#endif
|
||||
|
||||
#include "app/cycles_xml.h"
|
||||
#include "app/oiio_output_driver.h"
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
# include "opengl/display_driver.h"
|
||||
# include "opengl/window.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct Options {
|
||||
unique_ptr<Session> session;
|
||||
Scene *scene;
|
||||
string filepath;
|
||||
int width, height;
|
||||
SceneParams scene_params;
|
||||
SessionParams session_params;
|
||||
bool quiet;
|
||||
bool show_help, interactive, pause;
|
||||
string output_filepath;
|
||||
string output_pass;
|
||||
} options;
|
||||
|
||||
static void session_print(const string &str)
|
||||
{
|
||||
/* print with carriage return to overwrite previous */
|
||||
printf("\r%s", str.c_str());
|
||||
|
||||
/* add spaces to overwrite longer previous print */
|
||||
static int maxlen = 0;
|
||||
const int len = str.size();
|
||||
maxlen = max(len, maxlen);
|
||||
|
||||
for (int i = len; i < maxlen; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
/* flush because we don't write an end of line */
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void session_print_status()
|
||||
{
|
||||
string status;
|
||||
string substatus;
|
||||
|
||||
/* get status */
|
||||
const double progress = options.session->progress.get_progress();
|
||||
options.session->progress.get_status(status, substatus);
|
||||
|
||||
if (!substatus.empty()) {
|
||||
status += ": " + substatus;
|
||||
}
|
||||
|
||||
/* print status */
|
||||
status = string_printf("Progress %05.2f %s", progress * 100, status.c_str());
|
||||
session_print(status);
|
||||
}
|
||||
|
||||
static BufferParams &session_buffer_params()
|
||||
{
|
||||
static BufferParams buffer_params;
|
||||
buffer_params.width = options.width;
|
||||
buffer_params.height = options.height;
|
||||
buffer_params.full_width = options.width;
|
||||
buffer_params.full_height = options.height;
|
||||
|
||||
return buffer_params;
|
||||
}
|
||||
|
||||
static void scene_init()
|
||||
{
|
||||
options.scene = options.session->scene.get();
|
||||
|
||||
/* Read XML or USD */
|
||||
#ifdef WITH_USD
|
||||
if (!string_endswith(string_to_lower(options.filepath), ".xml")) {
|
||||
HD_CYCLES_NS::HdCyclesFileReader::read(options.session.get(), options.filepath.c_str());
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xml_read_file(options.scene, options.filepath.c_str());
|
||||
}
|
||||
|
||||
/* Camera width/height override? */
|
||||
if (!(options.width == 0 || options.height == 0)) {
|
||||
options.scene->camera->set_full_width(options.width);
|
||||
options.scene->camera->set_full_height(options.height);
|
||||
}
|
||||
else {
|
||||
options.width = options.scene->camera->get_full_width();
|
||||
options.height = options.scene->camera->get_full_height();
|
||||
}
|
||||
|
||||
/* Calculate Viewplane */
|
||||
options.scene->camera->compute_auto_viewplane();
|
||||
}
|
||||
|
||||
static void session_init()
|
||||
{
|
||||
options.output_pass = "combined";
|
||||
options.session = make_unique<Session>(options.session_params, options.scene_params);
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
if (!options.session_params.background) {
|
||||
options.session->set_display_driver(make_unique<OpenGLDisplayDriver>(
|
||||
window_opengl_context_enable, window_opengl_context_disable));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!options.output_filepath.empty()) {
|
||||
options.session->set_output_driver(make_unique<OIIOOutputDriver>(
|
||||
options.output_filepath, options.output_pass, session_print));
|
||||
}
|
||||
|
||||
if (options.session_params.background && !options.quiet) {
|
||||
options.session->progress.set_update_callback([] { session_print_status(); });
|
||||
}
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
else {
|
||||
options.session->progress.set_update_callback([] { window_redraw(); });
|
||||
}
|
||||
#endif
|
||||
|
||||
/* load scene */
|
||||
scene_init();
|
||||
|
||||
/* add pass for output. */
|
||||
Pass *pass = options.scene->create_node<Pass>();
|
||||
pass->set_name(ustring(options.output_pass.c_str()));
|
||||
pass->set_type(PASS_COMBINED);
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
options.session->start();
|
||||
}
|
||||
|
||||
static void session_exit()
|
||||
{
|
||||
if (options.session) {
|
||||
options.session.reset();
|
||||
}
|
||||
|
||||
if (options.session_params.background && !options.quiet) {
|
||||
session_print("Finished Rendering.");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
static void display_info(Progress &progress)
|
||||
{
|
||||
static double latency = 0.0;
|
||||
static double last = 0;
|
||||
const double elapsed = time_dt();
|
||||
string str;
|
||||
string interactive;
|
||||
|
||||
latency = (elapsed - last);
|
||||
last = elapsed;
|
||||
|
||||
double total_time;
|
||||
double sample_time;
|
||||
string status;
|
||||
string substatus;
|
||||
|
||||
progress.get_time(total_time, sample_time);
|
||||
progress.get_status(status, substatus);
|
||||
const double progress_val = progress.get_progress();
|
||||
|
||||
if (!substatus.empty()) {
|
||||
status += ": " + substatus;
|
||||
}
|
||||
|
||||
interactive = options.interactive ? "On" : "Off";
|
||||
|
||||
str = string_printf(
|
||||
"%s"
|
||||
" Time: %.2f"
|
||||
" Latency: %.4f"
|
||||
" Progress: %05.2f"
|
||||
" Average: %.4f"
|
||||
" Interactive: %s",
|
||||
status.c_str(),
|
||||
total_time,
|
||||
latency,
|
||||
progress_val * 100,
|
||||
sample_time,
|
||||
interactive.c_str());
|
||||
|
||||
window_display_info(str.c_str());
|
||||
|
||||
if (options.show_help) {
|
||||
window_display_help();
|
||||
}
|
||||
}
|
||||
|
||||
static void display()
|
||||
{
|
||||
options.session->draw();
|
||||
|
||||
display_info(options.session->progress);
|
||||
}
|
||||
|
||||
static void motion(const int x, const int y, int button)
|
||||
{
|
||||
if (options.interactive) {
|
||||
Transform matrix = options.session->scene->camera->get_matrix();
|
||||
|
||||
/* Translate */
|
||||
if (button == 0) {
|
||||
const float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
|
||||
matrix = matrix * transform_translate(translate);
|
||||
}
|
||||
|
||||
/* Rotate */
|
||||
else if (button == 2) {
|
||||
const float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f);
|
||||
matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w));
|
||||
|
||||
const float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f);
|
||||
matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w));
|
||||
}
|
||||
|
||||
/* Update and Reset */
|
||||
options.session->scene->camera->set_matrix(matrix);
|
||||
options.session->scene->camera->need_flags_update = true;
|
||||
options.session->scene->camera->need_device_update = true;
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
}
|
||||
|
||||
static void resize(const int width, const int height)
|
||||
{
|
||||
options.width = width;
|
||||
options.height = height;
|
||||
|
||||
if (options.session) {
|
||||
/* Update camera */
|
||||
options.session->scene->camera->set_full_width(options.width);
|
||||
options.session->scene->camera->set_full_height(options.height);
|
||||
options.session->scene->camera->compute_auto_viewplane();
|
||||
options.session->scene->camera->need_flags_update = true;
|
||||
options.session->scene->camera->need_device_update = true;
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard(unsigned char key)
|
||||
{
|
||||
/* Toggle help */
|
||||
if (key == 'h') {
|
||||
options.show_help = !(options.show_help);
|
||||
|
||||
/* Reset */
|
||||
}
|
||||
else if (key == 'r') {
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
|
||||
/* Cancel */
|
||||
}
|
||||
else if (key == 27) { // escape
|
||||
options.session->progress.set_cancel("Canceled");
|
||||
|
||||
/* Pause */
|
||||
}
|
||||
else if (key == 'p') {
|
||||
options.pause = !options.pause;
|
||||
options.session->set_pause(options.pause);
|
||||
}
|
||||
|
||||
/* Interactive Mode */
|
||||
else if (key == 'i') {
|
||||
options.interactive = !(options.interactive);
|
||||
|
||||
/* Navigation */
|
||||
}
|
||||
else if (options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
|
||||
Transform matrix = options.session->scene->camera->get_matrix();
|
||||
float3 translate;
|
||||
|
||||
if (key == 'w') {
|
||||
translate = make_float3(0.0f, 0.0f, 0.1f);
|
||||
}
|
||||
else if (key == 's') {
|
||||
translate = make_float3(0.0f, 0.0f, -0.1f);
|
||||
}
|
||||
else if (key == 'a') {
|
||||
translate = make_float3(-0.1f, 0.0f, 0.0f);
|
||||
}
|
||||
else if (key == 'd') {
|
||||
translate = make_float3(0.1f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
matrix = matrix * transform_translate(translate);
|
||||
|
||||
/* Update and Reset */
|
||||
options.session->scene->camera->set_matrix(matrix);
|
||||
options.session->scene->camera->need_flags_update = true;
|
||||
options.session->scene->camera->need_device_update = true;
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
|
||||
/* Set Max Bounces */
|
||||
else if (options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) {
|
||||
int bounce;
|
||||
switch (key) {
|
||||
case '0':
|
||||
bounce = 0;
|
||||
break;
|
||||
case '1':
|
||||
bounce = 1;
|
||||
break;
|
||||
case '2':
|
||||
bounce = 2;
|
||||
break;
|
||||
case '3':
|
||||
bounce = 3;
|
||||
break;
|
||||
default:
|
||||
bounce = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
options.session->scene->integrator->set_max_bounce(bounce);
|
||||
|
||||
options.session->reset(options.session_params, session_buffer_params());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void parse_int(OIIO::cspan<const char *> argv, int *i)
|
||||
{
|
||||
assert(argv.size() == 2);
|
||||
*i = atoi(argv[1]);
|
||||
}
|
||||
|
||||
static void parse_string(OIIO::cspan<const char *> argv, std::string *s)
|
||||
{
|
||||
assert(argv.size() == 2);
|
||||
*s = argv[1];
|
||||
}
|
||||
|
||||
static void options_parse(const int argc, const char **argv)
|
||||
{
|
||||
options.width = 1024;
|
||||
options.height = 512;
|
||||
options.filepath = "";
|
||||
options.session = nullptr;
|
||||
options.quiet = false;
|
||||
options.session_params.use_auto_tile = false;
|
||||
options.session_params.tile_size = 0;
|
||||
|
||||
/* device names */
|
||||
string device_names;
|
||||
string devicename = "CPU";
|
||||
bool list = false;
|
||||
|
||||
/* List devices for which support is compiled in. */
|
||||
const vector<DeviceType> types = Device::available_types();
|
||||
for (const DeviceType type : types) {
|
||||
if (!device_names.empty()) {
|
||||
device_names += ", ";
|
||||
}
|
||||
|
||||
device_names += Device::string_from_type(type);
|
||||
}
|
||||
|
||||
/* shading system */
|
||||
string ssname = "svm";
|
||||
|
||||
/* parse options */
|
||||
ArgParse ap;
|
||||
bool help = false;
|
||||
bool profile = false;
|
||||
bool version = false;
|
||||
string log_level;
|
||||
|
||||
ap.usage("cycles [options] file.xml");
|
||||
ap.arg("filename").hidden().action([&](auto argv) { options.filepath = argv[0]; });
|
||||
ap.arg("--device %s:DEVICE").help("Devices to use: " + device_names).action([&](auto argv) {
|
||||
parse_string(argv, &devicename);
|
||||
});
|
||||
#ifdef WITH_OSL
|
||||
ap.arg("--shadingsys %s:SHADINGSYSTEM")
|
||||
.help("Shading system to use: svm, osl")
|
||||
.action([&](auto argv) { parse_string(argv, &ssname); });
|
||||
#endif
|
||||
ap.arg("--background", &options.session_params.background)
|
||||
.help("Render in background, without user interface");
|
||||
ap.arg("--quiet", &options.quiet).help("In background mode, don't print progress messages");
|
||||
ap.arg("--samples %d:SAMPLES").help("Number of samples to render").action([&](auto argv) {
|
||||
parse_int(argv, &options.session_params.samples);
|
||||
});
|
||||
ap.arg("--output %s:OUTPUT").help("File path to write output image").action([&](auto argv) {
|
||||
parse_string(argv, &options.output_filepath);
|
||||
});
|
||||
ap.arg("--threads %d:THREADS").help("CPU Rendering Threads").action([&](auto argv) {
|
||||
parse_int(argv, &options.session_params.threads);
|
||||
});
|
||||
ap.arg("--width %d:WIDTH").help("Image width in pixelx").action([&](auto argv) {
|
||||
parse_int(argv, &options.width);
|
||||
});
|
||||
ap.arg("--height %d:HEIGHT").help("Image height in pixel").action([&](auto argv) {
|
||||
parse_int(argv, &options.height);
|
||||
});
|
||||
ap.arg("--tile-size %d:TILE_SIZE").help("Tile size in pixels").action([&](auto argv) {
|
||||
parse_int(argv, &options.session_params.tile_size);
|
||||
});
|
||||
ap.arg("--list-devices", &list).help("List information about all available devices");
|
||||
ap.arg("--profile", &profile).help("Enable profile logging");
|
||||
ap.arg("--log-level %s:LEVEL")
|
||||
.help("Log verbosity: fatal, error, warning, info, stats, debug")
|
||||
.action([&](auto argv) { parse_string(argv, &log_level); });
|
||||
ap.arg("--help", &help).help("Print help message");
|
||||
ap.arg("--version", &version).help("Print version number");
|
||||
|
||||
if (ap.parse_args(argc, argv) < 0) {
|
||||
fprintf(stderr, "%s\n", ap.geterror().c_str());
|
||||
ap.print_help();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!log_level.empty()) {
|
||||
log_level_set(log_level);
|
||||
}
|
||||
|
||||
if (list) {
|
||||
const vector<DeviceInfo> devices = Device::available_devices();
|
||||
printf("Devices:\n");
|
||||
|
||||
for (const DeviceInfo &info : devices) {
|
||||
printf(" %-10s%s%s\n",
|
||||
Device::string_from_type(info.type).c_str(),
|
||||
info.description.c_str(),
|
||||
(info.display_device) ? " (display)" : "");
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (version) {
|
||||
printf("%s\n", CYCLES_VERSION_STRING);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (help || options.filepath.empty()) {
|
||||
ap.print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
options.session_params.use_profiling = profile;
|
||||
|
||||
if (ssname == "osl") {
|
||||
options.scene_params.shadingsystem = SHADINGSYSTEM_OSL;
|
||||
}
|
||||
else if (ssname == "svm") {
|
||||
options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
|
||||
}
|
||||
|
||||
#ifndef WITH_CYCLES_STANDALONE_GUI
|
||||
options.session_params.background = true;
|
||||
#endif
|
||||
|
||||
if (options.session_params.tile_size > 0) {
|
||||
options.session_params.use_auto_tile = true;
|
||||
}
|
||||
|
||||
/* find matching device */
|
||||
const DeviceType device_type = Device::type_from_string(devicename.c_str());
|
||||
vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK(device_type));
|
||||
|
||||
bool device_available = false;
|
||||
if (!devices.empty()) {
|
||||
options.session_params.device = devices.front();
|
||||
device_available = true;
|
||||
}
|
||||
|
||||
/* handle invalid configurations */
|
||||
if (options.session_params.device.type == DEVICE_NONE || !device_available) {
|
||||
fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#ifdef WITH_OSL
|
||||
else if (!(ssname == "osl" || ssname == "svm")) {
|
||||
fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (options.scene_params.shadingsystem == SHADINGSYSTEM_OSL &&
|
||||
options.session_params.device.type != DEVICE_CPU)
|
||||
{
|
||||
fprintf(stderr, "OSL shading system only works with CPU device\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
else if (options.session_params.samples < 0) {
|
||||
fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (options.filepath.empty()) {
|
||||
fprintf(stderr, "No file path specified\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
using namespace ccl;
|
||||
|
||||
int main(const int argc, const char **argv)
|
||||
{
|
||||
log_init(nullptr);
|
||||
path_init();
|
||||
system_max_open_files_ensure();
|
||||
options_parse(argc, argv);
|
||||
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
if (options.session_params.background) {
|
||||
#endif
|
||||
session_init();
|
||||
options.session->wait();
|
||||
session_exit();
|
||||
#ifdef WITH_CYCLES_STANDALONE_GUI
|
||||
}
|
||||
else {
|
||||
const string title = "Cycles: " + path_filename(options.filepath);
|
||||
|
||||
/* init/exit are callback so they run while GL is initialized */
|
||||
window_main_loop(title.c_str(),
|
||||
options.width,
|
||||
options.height,
|
||||
session_init,
|
||||
session_exit,
|
||||
resize,
|
||||
display,
|
||||
keyboard,
|
||||
motion);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
884
blender-5.2.0/intern/cycles/app/cycles_xml.cpp
Normal file
884
blender-5.2.0/intern/cycles/app/cycles_xml.cpp
Normal file
@@ -0,0 +1,884 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#include "graph/node_xml.h"
|
||||
|
||||
#include "scene/background.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/film.h"
|
||||
#include "scene/integrator.h"
|
||||
#include "scene/light.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/osl.h"
|
||||
#include "scene/scene.h"
|
||||
#include "scene/shader.h"
|
||||
#include "scene/shader_graph.h"
|
||||
#include "scene/shader_nodes.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/path.h"
|
||||
#include "util/projection.h"
|
||||
#include "util/string.h"
|
||||
#include "util/transform.h"
|
||||
#include "util/xml.h"
|
||||
|
||||
#include "app/cycles_xml.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* XML reading state */
|
||||
|
||||
struct XMLReadState : public XMLReader {
|
||||
Scene *scene = nullptr; /* Scene pointer. */
|
||||
Transform tfm; /* Current transform state. */
|
||||
bool smooth = false; /* Smooth normal state. */
|
||||
Shader *shader = nullptr; /* Current shader. */
|
||||
string base; /* Base path to current file. */
|
||||
float dicing_rate = 1.0f; /* Current dicing rate. */
|
||||
Object *object = nullptr; /* Current object. */
|
||||
|
||||
XMLReadState()
|
||||
{
|
||||
tfm = transform_identity();
|
||||
}
|
||||
};
|
||||
|
||||
/* Attribute Reading */
|
||||
|
||||
static bool xml_read_int(int *value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
*value = atoi(attr.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_int_array(vector<int> &value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
vector<string> tokens;
|
||||
string_split(tokens, attr.value());
|
||||
|
||||
for (const string &token : tokens) {
|
||||
value.push_back(atoi(token.c_str()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float(float *value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
*value = (float)atof(attr.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float_array(vector<float> &value, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
vector<string> tokens;
|
||||
string_split(tokens, attr.value());
|
||||
|
||||
for (const string &token : tokens) {
|
||||
value.push_back((float)atof(token.c_str()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float3(float3 *value, const xml_node node, const char *name)
|
||||
{
|
||||
vector<float> array;
|
||||
|
||||
if (xml_read_float_array(array, node, name) && array.size() == 3) {
|
||||
*value = make_float3(array[0], array[1], array[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float3_array(vector<packed_float3> &value,
|
||||
const xml_node node,
|
||||
const char *name)
|
||||
{
|
||||
vector<float> array;
|
||||
|
||||
if (xml_read_float_array(array, node, name)) {
|
||||
for (size_t i = 0; i < array.size(); i += 3) {
|
||||
value.push_back(make_float3(array[i + 0], array[i + 1], array[i + 2]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_float4(float4 *value, const xml_node node, const char *name)
|
||||
{
|
||||
vector<float> array;
|
||||
|
||||
if (xml_read_float_array(array, node, name) && array.size() == 4) {
|
||||
*value = make_float4(array[0], array[1], array[2], array[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_read_string(string *str, const xml_node node, const char *name)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
*str = attr.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xml_equal_string(const xml_node node, const char *name, const char *value)
|
||||
{
|
||||
const xml_attribute attr = node.attribute(name);
|
||||
|
||||
if (attr) {
|
||||
return string_iequals(attr.value(), value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Camera */
|
||||
|
||||
static void xml_read_camera(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Camera *cam = state.scene->camera;
|
||||
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
xml_read_int(&width, node, "width");
|
||||
xml_read_int(&height, node, "height");
|
||||
|
||||
cam->set_full_width(width);
|
||||
cam->set_full_height(height);
|
||||
|
||||
xml_read_node(state, cam, node);
|
||||
|
||||
cam->set_matrix(state.tfm);
|
||||
|
||||
cam->need_flags_update = true;
|
||||
cam->update(state.scene);
|
||||
}
|
||||
|
||||
/* Shader */
|
||||
|
||||
static void xml_read_shader_graph(XMLReadState &state, Shader *shader, const xml_node graph_node)
|
||||
{
|
||||
xml_read_node(state, shader, graph_node);
|
||||
|
||||
unique_ptr<ShaderGraph> graph = make_unique<ShaderGraph>();
|
||||
|
||||
/* local state, shader nodes can't link to nodes outside the shader graph */
|
||||
XMLReader graph_reader;
|
||||
graph_reader.node_map[ustring("output")] = graph->output();
|
||||
|
||||
for (xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
|
||||
ustring node_name(node.name());
|
||||
|
||||
if (node_name == "connect") {
|
||||
/* connect nodes */
|
||||
vector<string> from_tokens;
|
||||
vector<string> to_tokens;
|
||||
|
||||
string_split(from_tokens, node.attribute("from").value());
|
||||
string_split(to_tokens, node.attribute("to").value());
|
||||
|
||||
if (from_tokens.size() == 2 && to_tokens.size() == 2) {
|
||||
const ustring from_node_name(from_tokens[0]);
|
||||
const ustring from_socket_name(from_tokens[1]);
|
||||
const ustring to_node_name(to_tokens[0]);
|
||||
const ustring to_socket_name(to_tokens[1]);
|
||||
|
||||
/* find nodes and sockets */
|
||||
ShaderOutput *output = nullptr;
|
||||
ShaderInput *input = nullptr;
|
||||
|
||||
if (graph_reader.node_map.contains(from_node_name)) {
|
||||
ShaderNode *fromnode = (ShaderNode *)graph_reader.node_map[from_node_name];
|
||||
|
||||
for (ShaderOutput *out : fromnode->outputs) {
|
||||
if (string_iequals(out->socket_type.name.string(), from_socket_name.string())) {
|
||||
output = out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output) {
|
||||
LOG_ERROR << "Unknown output socket name \"" << from_node_name << "\" on \""
|
||||
<< from_socket_name << "\".";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown shader node name \"" << from_node_name << "\"";
|
||||
}
|
||||
|
||||
if (graph_reader.node_map.contains(to_node_name)) {
|
||||
ShaderNode *tonode = (ShaderNode *)graph_reader.node_map[to_node_name];
|
||||
|
||||
for (ShaderInput *in : tonode->inputs) {
|
||||
if (string_iequals(in->socket_type.name.string(), to_socket_name.string())) {
|
||||
input = in;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
LOG_ERROR << "Unknown input socket name \"" << to_socket_name << "\" on \""
|
||||
<< to_node_name << "\"";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown shader node name \"" << to_node_name << "\"";
|
||||
}
|
||||
|
||||
/* connect */
|
||||
if (output && input) {
|
||||
graph->connect(output, input);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Invalid from or to value for connect node.";
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ShaderNode *snode = nullptr;
|
||||
|
||||
#ifdef WITH_OSL
|
||||
if (node_name == "osl_shader") {
|
||||
ShaderManager *manager = state.scene->shader_manager.get();
|
||||
|
||||
if (manager->use_osl()) {
|
||||
std::string filepath;
|
||||
|
||||
if (xml_read_string(&filepath, node, "src")) {
|
||||
if (path_is_relative(filepath)) {
|
||||
filepath = path_join(state.base, filepath);
|
||||
}
|
||||
|
||||
snode = OSLShaderManager::osl_node(graph.get(), state.scene, filepath, "");
|
||||
|
||||
if (!snode) {
|
||||
LOG_ERROR << "Failed to create OSL node from \"" << filepath << "\"";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "OSL node missing \"src\" attribute.";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "OSL node without using --shadingsys osl.";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* exception for name collision */
|
||||
if (node_name == "background") {
|
||||
node_name = "background_shader";
|
||||
}
|
||||
|
||||
const NodeType *node_type = NodeType::find(node_name);
|
||||
|
||||
if (!node_type) {
|
||||
LOG_ERROR << "Unknown shader node \"" << node.name() << "\"";
|
||||
continue;
|
||||
}
|
||||
if (node_type->type != NodeType::SHADER) {
|
||||
LOG_ERROR << "Node type \"" << node_type->name << "\" is not a shader node";
|
||||
continue;
|
||||
}
|
||||
if (node_type->create == nullptr) {
|
||||
LOG_ERROR << "Can't create abstract node type \""
|
||||
<< "\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
snode = graph->create_node(node_type);
|
||||
}
|
||||
|
||||
xml_read_node(graph_reader, snode, node);
|
||||
|
||||
if (node_name == "image_texture") {
|
||||
ImageTextureNode *img = (ImageTextureNode *)snode;
|
||||
const ustring filename(path_join(state.base, img->get_filename().string()));
|
||||
img->set_filename(filename);
|
||||
}
|
||||
else if (node_name == "environment_texture") {
|
||||
EnvironmentTextureNode *env = (EnvironmentTextureNode *)snode;
|
||||
const ustring filename(path_join(state.base, env->get_filename().string()));
|
||||
env->set_filename(filename);
|
||||
}
|
||||
}
|
||||
|
||||
shader->set_graph(std::move(graph));
|
||||
shader->tag_update(state.scene);
|
||||
}
|
||||
|
||||
static void xml_read_shader(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Shader *shader = state.scene->create_node<Shader>();
|
||||
xml_read_shader_graph(state, shader, node);
|
||||
}
|
||||
|
||||
/* Background */
|
||||
|
||||
static void xml_read_background(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
/* Background Settings */
|
||||
xml_read_node(state, state.scene->background, node);
|
||||
|
||||
/* Background Shader */
|
||||
Shader *shader = state.scene->default_background;
|
||||
xml_read_shader_graph(state, shader, node);
|
||||
}
|
||||
|
||||
/* Mesh */
|
||||
|
||||
static Mesh *xml_add_mesh(Scene *scene, const Transform &tfm, Object *object)
|
||||
{
|
||||
if (object && object->get_geometry()->is_mesh()) {
|
||||
/* Use existing object and mesh */
|
||||
object->set_tfm(tfm);
|
||||
Geometry *geometry = object->get_geometry();
|
||||
return static_cast<Mesh *>(geometry);
|
||||
}
|
||||
|
||||
/* Create mesh */
|
||||
Mesh *mesh = scene->create_node<Mesh>();
|
||||
|
||||
/* Create object. */
|
||||
object = scene->create_node<Object>();
|
||||
object->set_geometry(mesh);
|
||||
object->set_tfm(tfm);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void xml_read_mesh(const XMLReadState &state, const xml_node node)
|
||||
{
|
||||
/* add mesh */
|
||||
Mesh *mesh = xml_add_mesh(state.scene, state.tfm, state.object);
|
||||
array<Node *> used_shaders = mesh->get_used_shaders();
|
||||
used_shaders.push_back_slow(state.shader);
|
||||
mesh->set_used_shaders(used_shaders);
|
||||
|
||||
/* read state */
|
||||
const int shader = 0;
|
||||
const bool smooth = state.smooth;
|
||||
|
||||
/* read vertices and polygons */
|
||||
vector<packed_float3> P;
|
||||
vector<packed_float3> VN; /* Vertex normals */
|
||||
vector<float> UV;
|
||||
vector<float> T; /* UV tangents */
|
||||
vector<float> TS; /* UV tangent signs */
|
||||
vector<int> verts;
|
||||
vector<int> nverts;
|
||||
|
||||
xml_read_float3_array(P, node, "P");
|
||||
xml_read_int_array(verts, node, "verts");
|
||||
xml_read_int_array(nverts, node, "nverts");
|
||||
|
||||
if (xml_equal_string(node, "subdivision", "catmull-clark")) {
|
||||
mesh->set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
|
||||
}
|
||||
else if (xml_equal_string(node, "subdivision", "linear")) {
|
||||
mesh->set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
|
||||
}
|
||||
|
||||
if (mesh->get_subdivision_type() == Mesh::SUBDIVISION_NONE) {
|
||||
/* create vertices */
|
||||
|
||||
size_t num_triangles = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
num_triangles += nverts[i] - 2;
|
||||
}
|
||||
mesh->resize_mesh(P.size(), num_triangles);
|
||||
std::copy_n(P.data(), P.size(), mesh->get_position_for_write());
|
||||
|
||||
int *triangles = mesh->get_triangles().data();
|
||||
|
||||
/* create triangles */
|
||||
int tri_index = 0;
|
||||
int index_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = verts[index_offset];
|
||||
const int v1 = verts[index_offset + j + 1];
|
||||
const int v2 = verts[index_offset + j + 2];
|
||||
|
||||
assert(v0 < (int)P.size());
|
||||
assert(v1 < (int)P.size());
|
||||
assert(v2 < (int)P.size());
|
||||
|
||||
triangles[tri_index * 3 + 0] = v0;
|
||||
triangles[tri_index * 3 + 1] = v1;
|
||||
triangles[tri_index * 3 + 2] = v2;
|
||||
tri_index++;
|
||||
}
|
||||
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
std::ranges::fill(mesh->get_smooth(), smooth);
|
||||
std::ranges::fill(mesh->get_shader(), shader);
|
||||
|
||||
mesh->tag_triangles_modified();
|
||||
mesh->tag_shader_modified();
|
||||
mesh->tag_smooth_modified();
|
||||
|
||||
/* Vertex normals */
|
||||
if (xml_read_float3_array(VN, node, Attribute::standard_name(ATTR_STD_VERTEX_NORMAL))) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
packed_normal *fdata = attr->data_for_write<packed_normal>();
|
||||
|
||||
/* Loop over the normals */
|
||||
for (auto n : VN) {
|
||||
fdata[0] = packed_normal(n);
|
||||
fdata++;
|
||||
}
|
||||
}
|
||||
|
||||
/* UV map */
|
||||
if (xml_read_float_array(UV, node, "UV") ||
|
||||
xml_read_float_array(UV, node, Attribute::standard_name(ATTR_STD_UV)))
|
||||
{
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_UV);
|
||||
float2 *fdata = attr->data_for_write<float2>();
|
||||
|
||||
/* Loop over the triangles */
|
||||
index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = index_offset;
|
||||
const int v1 = index_offset + j + 1;
|
||||
const int v2 = index_offset + j + 2;
|
||||
|
||||
assert(v0 * 2 + 1 < (int)UV.size());
|
||||
assert(v1 * 2 + 1 < (int)UV.size());
|
||||
assert(v2 * 2 + 1 < (int)UV.size());
|
||||
|
||||
fdata[0] = make_float2(UV[v0 * 2], UV[v0 * 2 + 1]);
|
||||
fdata[1] = make_float2(UV[v1 * 2], UV[v1 * 2 + 1]);
|
||||
fdata[2] = make_float2(UV[v2 * 2], UV[v2 * 2 + 1]);
|
||||
fdata += 3;
|
||||
}
|
||||
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tangents */
|
||||
if (xml_read_float_array(T, node, Attribute::standard_name(ATTR_STD_UV_TANGENT))) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_UV_TANGENT);
|
||||
packed_float3 *fdata = attr->data_for_write<packed_float3>();
|
||||
|
||||
/* Loop over the triangles */
|
||||
index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = index_offset;
|
||||
const int v1 = index_offset + j + 1;
|
||||
const int v2 = index_offset + j + 2;
|
||||
|
||||
assert(v0 * 3 + 2 < (int)T.size());
|
||||
assert(v1 * 3 + 2 < (int)T.size());
|
||||
assert(v2 * 3 + 2 < (int)T.size());
|
||||
|
||||
fdata[0] = make_float3(T[v0 * 3], T[v0 * 3 + 1], T[v0 * 3 + 2]);
|
||||
fdata[1] = make_float3(T[v1 * 3], T[v1 * 3 + 1], T[v1 * 3 + 2]);
|
||||
fdata[2] = make_float3(T[v2 * 3], T[v2 * 3 + 1], T[v2 * 3 + 2]);
|
||||
fdata += 3;
|
||||
}
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tangent signs */
|
||||
if (xml_read_float_array(TS, node, Attribute::standard_name(ATTR_STD_UV_TANGENT_SIGN))) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_UV_TANGENT_SIGN);
|
||||
float *fdata = attr->data_for_write<float>();
|
||||
|
||||
/* Loop over the triangles */
|
||||
index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i] - 2; j++) {
|
||||
const int v0 = index_offset;
|
||||
const int v1 = index_offset + j + 1;
|
||||
const int v2 = index_offset + j + 2;
|
||||
|
||||
assert(v0 < (int)TS.size());
|
||||
assert(v1 < (int)TS.size());
|
||||
assert(v2 < (int)TS.size());
|
||||
|
||||
fdata[0] = TS[v0];
|
||||
fdata[1] = TS[v1];
|
||||
fdata[2] = TS[v2];
|
||||
fdata += 3;
|
||||
}
|
||||
index_offset += nverts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* create vertices */
|
||||
mesh->resize_mesh(P.size(), 0);
|
||||
|
||||
size_t num_corners = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
num_corners += nverts[i];
|
||||
}
|
||||
mesh->resize_subd_faces(nverts.size(), num_corners);
|
||||
|
||||
Attribute *subd_attr_P = mesh->subd_attributes.add(ATTR_STD_POSITION);
|
||||
subd_attr_P->resize(P.size());
|
||||
std::copy_n(P.data(), P.size(), subd_attr_P->data_for_write<packed_float3>());
|
||||
|
||||
int *subd_start_corner = mesh->get_subd_start_corner().data();
|
||||
int *subd_num_corners = mesh->get_subd_num_corners().data();
|
||||
int *subd_ptex_offset = mesh->get_subd_ptex_offset().data();
|
||||
int *subd_face_corners = mesh->get_subd_face_corners().data();
|
||||
|
||||
std::ranges::fill(mesh->get_subd_shader(), shader);
|
||||
std::ranges::fill(mesh->get_subd_smooth(), smooth);
|
||||
|
||||
std::ranges::copy(verts, subd_face_corners);
|
||||
|
||||
/* create subd_faces */
|
||||
int corner_index = 0;
|
||||
int ptex_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
subd_start_corner[i] = corner_index;
|
||||
subd_num_corners[i] = nverts[i];
|
||||
corner_index += nverts[i];
|
||||
|
||||
subd_ptex_offset[i] = ptex_offset;
|
||||
const int num_ptex = (nverts[i] == 4) ? 1 : nverts[i];
|
||||
ptex_offset += num_ptex;
|
||||
}
|
||||
|
||||
mesh->tag_subd_face_corners_modified();
|
||||
mesh->tag_subd_start_corner_modified();
|
||||
mesh->tag_subd_num_corners_modified();
|
||||
mesh->tag_subd_shader_modified();
|
||||
mesh->tag_subd_smooth_modified();
|
||||
mesh->tag_subd_ptex_offset_modified();
|
||||
|
||||
/* UV map */
|
||||
if (xml_read_float_array(UV, node, "UV") ||
|
||||
xml_read_float_array(UV, node, Attribute::standard_name(ATTR_STD_UV)))
|
||||
{
|
||||
Attribute *attr = mesh->subd_attributes.add(ATTR_STD_UV);
|
||||
packed_float3 *fdata = attr->data_for_write<packed_float3>();
|
||||
|
||||
int index_offset = 0;
|
||||
for (size_t i = 0; i < nverts.size(); i++) {
|
||||
for (int j = 0; j < nverts[i]; j++) {
|
||||
*(fdata++) = make_float3(UV[index_offset++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* setup subd params */
|
||||
float dicing_rate = state.dicing_rate;
|
||||
xml_read_float(&dicing_rate, node, "dicing_rate");
|
||||
dicing_rate = std::max(0.1f, dicing_rate);
|
||||
|
||||
mesh->set_subd_dicing_rate(dicing_rate);
|
||||
mesh->set_subd_objecttoworld(state.tfm);
|
||||
}
|
||||
|
||||
/* we don't yet support arbitrary attributes, for now add vertex
|
||||
* coordinates as generated coordinates if requested */
|
||||
if (mesh->need_attribute(state.scene, ATTR_STD_GENERATED)) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
||||
std::copy_n(mesh->get_position(), mesh->num_verts(), attr->data_for_write<packed_float3>());
|
||||
}
|
||||
}
|
||||
|
||||
/* Light */
|
||||
|
||||
static void xml_read_light(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Scene *scene = state.scene;
|
||||
|
||||
/* Create light. */
|
||||
string light_type;
|
||||
if (!xml_read_string(&light_type, node, "light_type")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Light *light;
|
||||
if (light_type == "point") {
|
||||
light = scene->create_node<PointLight>();
|
||||
}
|
||||
else if (light_type == "sun") {
|
||||
light = scene->create_node<SunLight>();
|
||||
}
|
||||
else if (light_type == "background") {
|
||||
light = scene->create_node<BackgroundLight>();
|
||||
}
|
||||
else if (light_type == "area") {
|
||||
light = scene->create_node<AreaLight>();
|
||||
}
|
||||
else {
|
||||
assert(light_type == "spot");
|
||||
light = scene->create_node<SpotLight>();
|
||||
}
|
||||
|
||||
array<Node *> used_shaders;
|
||||
used_shaders.push_back_slow(state.shader);
|
||||
light->set_used_shaders(used_shaders);
|
||||
|
||||
/* Create object. */
|
||||
Object *object = scene->create_node<Object>();
|
||||
object->set_tfm(state.tfm);
|
||||
object->set_visibility(PATH_RAY_VISIBILITY_ALL & ~PATH_RAY_VISIBILITY_CAMERA);
|
||||
object->set_geometry(light);
|
||||
|
||||
xml_read_node(state, light, node);
|
||||
}
|
||||
|
||||
/* Transform */
|
||||
|
||||
static void xml_read_transform(const xml_node node, Transform &tfm)
|
||||
{
|
||||
if (node.attribute("matrix")) {
|
||||
vector<float> matrix;
|
||||
if (xml_read_float_array(matrix, node, "matrix") && matrix.size() == 16) {
|
||||
const ProjectionTransform projection = *(ProjectionTransform *)matrix.data();
|
||||
tfm = tfm * projection_to_transform(projection_transpose(projection));
|
||||
}
|
||||
}
|
||||
|
||||
if (node.attribute("translate")) {
|
||||
float3 translate = zero_float3();
|
||||
xml_read_float3(&translate, node, "translate");
|
||||
tfm = tfm * transform_translate(translate);
|
||||
}
|
||||
|
||||
if (node.attribute("rotate")) {
|
||||
float4 rotate = zero_float4();
|
||||
xml_read_float4(&rotate, node, "rotate");
|
||||
tfm = tfm * transform_rotate(DEG2RADF(rotate.x), make_float3(rotate.y, rotate.z, rotate.w));
|
||||
}
|
||||
|
||||
if (node.attribute("scale")) {
|
||||
float3 scale = zero_float3();
|
||||
xml_read_float3(&scale, node, "scale");
|
||||
tfm = tfm * transform_scale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
/* State */
|
||||
|
||||
static void xml_read_state(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
/* Read shader */
|
||||
string shadername;
|
||||
|
||||
if (xml_read_string(&shadername, node, "shader")) {
|
||||
bool found = false;
|
||||
|
||||
for (Shader *shader : state.scene->shaders) {
|
||||
if (shader->name == shadername) {
|
||||
state.shader = shader;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOG_ERROR << "Unknown shader \"" << shadername << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
/* Read object */
|
||||
string objectname;
|
||||
|
||||
if (xml_read_string(&objectname, node, "object")) {
|
||||
bool found = false;
|
||||
|
||||
for (Object *object : state.scene->objects) {
|
||||
if (object->name == objectname) {
|
||||
state.object = object;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOG_ERROR << "Unknown object \"" << objectname << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
xml_read_float(&state.dicing_rate, node, "dicing_rate");
|
||||
|
||||
/* read smooth/flat */
|
||||
if (xml_equal_string(node, "interpolation", "smooth")) {
|
||||
state.smooth = true;
|
||||
}
|
||||
else if (xml_equal_string(node, "interpolation", "flat")) {
|
||||
state.smooth = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Object */
|
||||
|
||||
static void xml_read_object(XMLReadState &state, const xml_node node)
|
||||
{
|
||||
Scene *scene = state.scene;
|
||||
|
||||
/* create mesh */
|
||||
Mesh *mesh = scene->create_node<Mesh>();
|
||||
|
||||
/* create object */
|
||||
Object *object = scene->create_node<Object>();
|
||||
object->set_geometry(mesh);
|
||||
object->set_tfm(state.tfm);
|
||||
|
||||
xml_read_node(state, object, node);
|
||||
}
|
||||
|
||||
/* Scene */
|
||||
|
||||
static void xml_read_include(XMLReadState &state, const string &src);
|
||||
|
||||
static void xml_read_scene(XMLReadState &state, const xml_node scene_node)
|
||||
{
|
||||
for (xml_node node = scene_node.first_child(); node; node = node.next_sibling()) {
|
||||
if (string_iequals(node.name(), "film")) {
|
||||
xml_read_node(state, state.scene->film, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "integrator")) {
|
||||
xml_read_node(state, state.scene->integrator, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "camera")) {
|
||||
xml_read_camera(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "shader")) {
|
||||
xml_read_shader(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "background")) {
|
||||
xml_read_background(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "mesh")) {
|
||||
xml_read_mesh(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "light")) {
|
||||
xml_read_light(state, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "transform")) {
|
||||
XMLReadState substate = state;
|
||||
|
||||
xml_read_transform(node, substate.tfm);
|
||||
xml_read_scene(substate, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "state")) {
|
||||
XMLReadState substate = state;
|
||||
|
||||
xml_read_state(substate, node);
|
||||
xml_read_scene(substate, node);
|
||||
}
|
||||
else if (string_iequals(node.name(), "include")) {
|
||||
string src;
|
||||
|
||||
if (xml_read_string(&src, node, "src")) {
|
||||
xml_read_include(state, src);
|
||||
}
|
||||
}
|
||||
else if (string_iequals(node.name(), "object")) {
|
||||
XMLReadState substate = state;
|
||||
|
||||
xml_read_object(substate, node);
|
||||
xml_read_scene(substate, node);
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown node \"" << node.name() << "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Include */
|
||||
|
||||
static void xml_read_include(XMLReadState &state, const string &src)
|
||||
{
|
||||
/* open XML document */
|
||||
xml_document doc;
|
||||
xml_parse_result parse_result;
|
||||
|
||||
const string path = path_join(state.base, src);
|
||||
parse_result = doc.load_file(path.c_str());
|
||||
|
||||
if (parse_result) {
|
||||
XMLReadState substate = state;
|
||||
substate.base = path_dirname(path);
|
||||
|
||||
const xml_node cycles = doc.child("cycles");
|
||||
xml_read_scene(substate, cycles);
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "\"" << src << "\" read error: " << parse_result.description();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* File */
|
||||
|
||||
void xml_read_file(Scene *scene, const char *filepath)
|
||||
{
|
||||
XMLReadState state;
|
||||
|
||||
state.scene = scene;
|
||||
state.tfm = transform_identity();
|
||||
state.shader = scene->default_surface;
|
||||
state.smooth = false;
|
||||
state.dicing_rate = 1.0f;
|
||||
state.base = path_dirname(filepath);
|
||||
|
||||
xml_read_include(state, path_filename(filepath));
|
||||
|
||||
scene->params.bvh_type = BVH_TYPE_STATIC;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
19
blender-5.2.0/intern/cycles/app/cycles_xml.h
Normal file
19
blender-5.2.0/intern/cycles/app/cycles_xml.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/math_base.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Scene;
|
||||
|
||||
void xml_read_file(Scene *scene, const char *filepath);
|
||||
|
||||
/* macros for importing */
|
||||
#define RAD2DEGF(_rad) ((_rad) * (float)(180.0f / M_PI_F))
|
||||
#define DEG2RADF(_deg) ((_deg) * (float)(M_PI_F / 180.0f))
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
153
blender-5.2.0/intern/cycles/app/io_export_cycles_xml.py
Normal file
153
blender-5.2.0/intern/cycles/app/io_export_cycles_xml.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# XML exporter for generating test files, not intended for end users
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
import xml.dom.minidom as dom
|
||||
|
||||
import bpy
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
from bpy.props import PointerProperty, StringProperty
|
||||
|
||||
|
||||
def strip(root):
|
||||
root.text = None
|
||||
root.tail = None
|
||||
|
||||
for elem in root:
|
||||
strip(elem)
|
||||
|
||||
|
||||
def write(node, fname):
|
||||
strip(node)
|
||||
|
||||
s = etree.tostring(node)
|
||||
s = dom.parseString(s).toprettyxml()
|
||||
|
||||
f = open(fname, "w")
|
||||
f.write(s)
|
||||
|
||||
|
||||
class CyclesXMLSettings(bpy.types.PropertyGroup):
|
||||
@classmethod
|
||||
def register(cls):
|
||||
bpy.types.Scene.cycles_xml = PointerProperty(
|
||||
type=cls,
|
||||
name="Cycles XML export Settings",
|
||||
description="Cycles XML export settings",
|
||||
)
|
||||
cls.filepath = StringProperty(
|
||||
name='Filepath',
|
||||
description='Filepath for the .xml file',
|
||||
maxlen=256,
|
||||
default='',
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def unregister(cls):
|
||||
del bpy.types.Scene.cycles_xml
|
||||
|
||||
|
||||
# User Interface Drawing Code.
|
||||
|
||||
|
||||
class RenderButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "render"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.engine == 'CYCLES'
|
||||
|
||||
|
||||
class PHYSICS_PT_fluid_export(RenderButtonsPanel, bpy.types.Panel):
|
||||
bl_label = "Cycles XML Exporter"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
cycles = context.scene.cycles_xml
|
||||
|
||||
# layout.prop(cycles, "filepath")
|
||||
layout.operator("export_mesh.cycles_xml")
|
||||
|
||||
|
||||
# Export Operator
|
||||
class ExportCyclesXML(bpy.types.Operator, ExportHelper):
|
||||
bl_idname = "export_mesh.cycles_xml"
|
||||
bl_label = "Export Cycles XML"
|
||||
|
||||
filename_ext = ".xml"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.active_object is not None)
|
||||
|
||||
def execute(self, context):
|
||||
filepath = bpy.path.ensure_ext(self.filepath, ".xml")
|
||||
|
||||
# get mesh
|
||||
scene = context.scene
|
||||
object = context.active_object
|
||||
|
||||
if not object:
|
||||
raise Exception("No active object")
|
||||
|
||||
mesh = object.to_mesh(scene, True, 'PREVIEW')
|
||||
|
||||
if not mesh:
|
||||
raise Exception("No mesh data in active object")
|
||||
|
||||
# generate mesh node
|
||||
nverts = ""
|
||||
verts = ""
|
||||
uvs = ""
|
||||
P = ""
|
||||
|
||||
for v in mesh.vertices:
|
||||
P += "%f %f %f " % (v.co[0], v.co[1], v.co[2])
|
||||
|
||||
verts_and_uvs = zip(mesh.tessfaces, mesh.tessface_uv_textures.active.data)
|
||||
|
||||
for f, uvf in verts_and_uvs:
|
||||
vcount = len(f.vertices)
|
||||
nverts += str(vcount) + " "
|
||||
|
||||
for v in f.vertices:
|
||||
verts += str(v) + " "
|
||||
|
||||
uvs += str(uvf.uv1[0]) + " " + str(uvf.uv1[1]) + " "
|
||||
uvs += str(uvf.uv2[0]) + " " + str(uvf.uv2[1]) + " "
|
||||
uvs += str(uvf.uv3[0]) + " " + str(uvf.uv3[1]) + " "
|
||||
if vcount == 4:
|
||||
uvs += " " + str(uvf.uv4[0]) + " " + str(uvf.uv4[1]) + " "
|
||||
|
||||
node = etree.Element(
|
||||
'mesh',
|
||||
attrib={
|
||||
'nverts': nverts.strip(),
|
||||
'verts': verts.strip(),
|
||||
'P': P,
|
||||
'UV': uvs.strip(),
|
||||
})
|
||||
|
||||
# write to file
|
||||
write(node, filepath)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
77
blender-5.2.0/intern/cycles/app/oiio_output_driver.cpp
Normal file
77
blender-5.2.0/intern/cycles/app/oiio_output_driver.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "app/oiio_output_driver.h"
|
||||
|
||||
#include "util/colorspace.h"
|
||||
#include "util/image.h"
|
||||
#include "util/unique_ptr.h"
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/imagebufalgo.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
OIIOOutputDriver::OIIOOutputDriver(const string_view filepath,
|
||||
const string_view pass,
|
||||
LogFunction log)
|
||||
: filepath_(filepath), pass_(pass), log_(log)
|
||||
{
|
||||
}
|
||||
|
||||
OIIOOutputDriver::~OIIOOutputDriver() = default;
|
||||
|
||||
void OIIOOutputDriver::write_render_tile(const Tile &tile)
|
||||
{
|
||||
/* Only write the full buffer, no intermediate tiles. */
|
||||
if (!(tile.size == tile.full_size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_(string_printf("Writing image %s", filepath_.c_str()));
|
||||
|
||||
unique_ptr<ImageOutput> image_output(ImageOutput::create(filepath_));
|
||||
if (image_output == nullptr) {
|
||||
log_("Failed to create image file");
|
||||
return;
|
||||
}
|
||||
|
||||
const int width = tile.size.x;
|
||||
const int height = tile.size.y;
|
||||
|
||||
const ImageSpec spec(width, height, 4, TypeDesc::FLOAT);
|
||||
if (!image_output->open(filepath_, spec)) {
|
||||
log_("Failed to create image file");
|
||||
return;
|
||||
}
|
||||
|
||||
vector<float> pixels(width * height * 4);
|
||||
if (!tile.get_pass_pixels(pass_, 4, pixels.data())) {
|
||||
log_("Failed to read render pass pixels");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Manipulate offset and stride to convert from bottom-up to top-down convention. */
|
||||
OIIO::ImageBuf image_buffer(spec,
|
||||
pixels.data() + (height - 1) * width * 4,
|
||||
AutoStride,
|
||||
-width * 4 * sizeof(float),
|
||||
AutoStride);
|
||||
|
||||
/* Apply gamma correction for (some) non-linear file formats.
|
||||
* TODO: use OpenColorIO view transform if available. */
|
||||
if (ColorSpaceManager::detect_known_colorspace(
|
||||
u_colorspace_auto, "", image_output->format_name(), true) == u_colorspace_srgb)
|
||||
{
|
||||
const float g = 1.0f / 2.2f;
|
||||
OIIO::ImageBufAlgo::pow(image_buffer, image_buffer, {g, g, g, 1.0f});
|
||||
}
|
||||
|
||||
/* Write to disk and close */
|
||||
image_buffer.set_write_format(TypeDesc::FLOAT);
|
||||
image_buffer.write(image_output.get());
|
||||
image_output->close();
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/app/oiio_output_driver.h
Normal file
28
blender-5.2.0/intern/cycles/app/oiio_output_driver.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "session/output_driver.h"
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class OIIOOutputDriver : public OutputDriver {
|
||||
public:
|
||||
using LogFunction = std::function<void(const string &)>;
|
||||
|
||||
OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log);
|
||||
~OIIOOutputDriver() override;
|
||||
|
||||
void write_render_tile(const Tile &tile) override;
|
||||
|
||||
protected:
|
||||
string filepath_;
|
||||
string pass_;
|
||||
LogFunction log_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
401
blender-5.2.0/intern/cycles/app/opengl/display_driver.cpp
Normal file
401
blender-5.2.0/intern/cycles/app/opengl/display_driver.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "app/opengl/display_driver.h"
|
||||
#include "app/opengl/shader.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OpenGLDisplayDriver.
|
||||
*/
|
||||
|
||||
OpenGLDisplayDriver::OpenGLDisplayDriver(const std::function<bool()> &gl_context_enable,
|
||||
const std::function<void()> &gl_context_disable)
|
||||
: gl_context_enable_(gl_context_enable), gl_context_disable_(gl_context_disable)
|
||||
{
|
||||
}
|
||||
|
||||
OpenGLDisplayDriver::~OpenGLDisplayDriver() = default;
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Update procedure.
|
||||
*/
|
||||
|
||||
void OpenGLDisplayDriver::next_tile_begin()
|
||||
{
|
||||
/* Assuming no tiles used in interactive display. */
|
||||
}
|
||||
|
||||
bool OpenGLDisplayDriver::update_begin(const Params ¶ms,
|
||||
const int texture_width,
|
||||
const int texture_height)
|
||||
{
|
||||
/* Note that it's the responsibility of OpenGLDisplayDriver to ensure updating and drawing
|
||||
* the texture does not happen at the same time. This is achieved indirectly.
|
||||
*
|
||||
* When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
|
||||
* This same lock is also held when do_draw() is called, which together ensure mutual
|
||||
* exclusion.
|
||||
*
|
||||
* This locking is not performed on the Cycles side, because that would cause lock inversion. */
|
||||
if (!gl_context_enable_()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gl_render_sync_) {
|
||||
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
|
||||
if (!gl_texture_resources_ensure()) {
|
||||
gl_context_disable_();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Update texture dimensions if needed. */
|
||||
if (texture_.width != texture_width || texture_.height != texture_height) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA16F,
|
||||
texture_width,
|
||||
texture_height,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_HALF_FLOAT,
|
||||
nullptr);
|
||||
texture_.width = texture_width;
|
||||
texture_.height = texture_height;
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
/* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to
|
||||
* avoid undefined content. */
|
||||
texture_.need_zero = true;
|
||||
graphics_interop_buffer_.clear();
|
||||
}
|
||||
|
||||
/* Update PBO dimensions if needed.
|
||||
*
|
||||
* NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
|
||||
* at a resolution divider 1. This was we don't need to recreate graphics interoperability
|
||||
* objects which are costly and which are tied to the specific underlying buffer size.
|
||||
* The downside of this approach is that when graphics interoperability is not used we are
|
||||
* sending too much data to GPU when resolution divider is not 1. */
|
||||
const int buffer_width = params.full_size.x;
|
||||
const int buffer_height = params.full_size.y;
|
||||
if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) {
|
||||
const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
texture_.buffer_width = buffer_width;
|
||||
texture_.buffer_height = buffer_height;
|
||||
}
|
||||
|
||||
/* New content will be provided to the texture in one way or another, so mark this in a
|
||||
* centralized place. */
|
||||
texture_.need_update = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::update_end()
|
||||
{
|
||||
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
|
||||
gl_context_disable_();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Texture buffer mapping.
|
||||
*/
|
||||
|
||||
half4 *OpenGLDisplayDriver::map_texture_buffer()
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
|
||||
|
||||
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
|
||||
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
|
||||
if (!mapped_rgba_pixels) {
|
||||
LOG_ERROR << "Error mapping OpenGLDisplayDriver pixel buffer object.";
|
||||
}
|
||||
|
||||
if (texture_.need_zero) {
|
||||
const int64_t texture_width = texture_.width;
|
||||
const int64_t texture_height = texture_.height;
|
||||
memset(reinterpret_cast<void *>(mapped_rgba_pixels),
|
||||
0,
|
||||
texture_width * texture_height * sizeof(half4));
|
||||
texture_.need_zero = false;
|
||||
}
|
||||
|
||||
return mapped_rgba_pixels;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::unmap_texture_buffer()
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Graphics interoperability.
|
||||
*/
|
||||
|
||||
GraphicsInteropDevice OpenGLDisplayDriver::graphics_interop_get_device()
|
||||
{
|
||||
GraphicsInteropDevice interop_device;
|
||||
interop_device.type = GraphicsInteropDevice::OPENGL;
|
||||
return interop_device;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::graphics_interop_update_buffer()
|
||||
{
|
||||
if (graphics_interop_buffer_.is_empty()) {
|
||||
graphics_interop_buffer_.assign(GraphicsInteropDevice::OPENGL,
|
||||
texture_.gl_pbo_id,
|
||||
texture_.buffer_width * texture_.buffer_height *
|
||||
sizeof(half4));
|
||||
}
|
||||
|
||||
if (texture_.need_zero) {
|
||||
graphics_interop_buffer_.zero();
|
||||
texture_.need_zero = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::graphics_interop_activate()
|
||||
{
|
||||
gl_context_enable_();
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::graphics_interop_deactivate()
|
||||
{
|
||||
gl_context_disable_();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Drawing.
|
||||
*/
|
||||
|
||||
void OpenGLDisplayDriver::zero()
|
||||
{
|
||||
texture_.need_zero = true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::draw(const Params ¶ms)
|
||||
{
|
||||
/* See do_update_begin() for why no locking is required here. */
|
||||
if (texture_.need_zero) {
|
||||
/* Texture is requested to be cleared and was not yet cleared.
|
||||
* Do early return which should be equivalent of drawing all-zero texture. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gl_draw_resources_ensure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl_upload_sync_) {
|
||||
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
display_shader_.bind(params.full_size.x, params.full_size.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
|
||||
|
||||
if (texture_.width != params.size.x || texture_.height != params.size.y) {
|
||||
/* Resolution divider is different from 1, force nearest interpolation. */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
|
||||
|
||||
texture_update_if_needed();
|
||||
vertex_buffer_update(params);
|
||||
|
||||
GLuint vertex_array_object;
|
||||
glGenVertexArrays(1, &vertex_array_object);
|
||||
glBindVertexArray(vertex_array_object);
|
||||
|
||||
const int texcoord_attribute = display_shader_.get_tex_coord_attrib_location();
|
||||
const int position_attribute = display_shader_.get_position_attrib_location();
|
||||
|
||||
glEnableVertexAttribArray(texcoord_attribute);
|
||||
glEnableVertexAttribArray(position_attribute);
|
||||
|
||||
glVertexAttribPointer(
|
||||
texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)nullptr);
|
||||
glVertexAttribPointer(position_attribute,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
4 * sizeof(float),
|
||||
(const GLvoid *)(sizeof(float) * 2));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glDeleteVertexArrays(1, &vertex_array_object);
|
||||
|
||||
display_shader_.unbind();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
}
|
||||
|
||||
bool OpenGLDisplayDriver::gl_draw_resources_ensure()
|
||||
{
|
||||
if (!texture_.gl_id) {
|
||||
/* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
|
||||
* can not continue. Note that this is not an unrecoverable error, so once the texture is known
|
||||
* we will come back here and create all the GPU resources needed for draw. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gl_draw_resource_creation_attempted_) {
|
||||
return gl_draw_resources_created_;
|
||||
}
|
||||
gl_draw_resource_creation_attempted_ = true;
|
||||
|
||||
if (!vertex_buffer_) {
|
||||
glGenBuffers(1, &vertex_buffer_);
|
||||
if (!vertex_buffer_) {
|
||||
LOG_ERROR << "Error creating vertex buffer.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gl_draw_resources_created_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::gl_resources_destroy()
|
||||
{
|
||||
gl_context_enable_();
|
||||
|
||||
if (vertex_buffer_ != 0) {
|
||||
glDeleteBuffers(1, &vertex_buffer_);
|
||||
}
|
||||
|
||||
if (texture_.gl_pbo_id) {
|
||||
glDeleteBuffers(1, &texture_.gl_pbo_id);
|
||||
texture_.gl_pbo_id = 0;
|
||||
}
|
||||
|
||||
if (texture_.gl_id) {
|
||||
glDeleteTextures(1, &texture_.gl_id);
|
||||
texture_.gl_id = 0;
|
||||
}
|
||||
|
||||
gl_context_disable_();
|
||||
}
|
||||
|
||||
bool OpenGLDisplayDriver::gl_texture_resources_ensure()
|
||||
{
|
||||
if (texture_.creation_attempted) {
|
||||
return texture_.is_created;
|
||||
}
|
||||
texture_.creation_attempted = true;
|
||||
|
||||
DCHECK(!texture_.gl_id);
|
||||
DCHECK(!texture_.gl_pbo_id);
|
||||
|
||||
/* Create texture. */
|
||||
glGenTextures(1, &texture_.gl_id);
|
||||
if (!texture_.gl_id) {
|
||||
LOG_ERROR << "Error creating texture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Configure the texture. */
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
/* Create PBO for the texture. */
|
||||
glGenBuffers(1, &texture_.gl_pbo_id);
|
||||
if (!texture_.gl_pbo_id) {
|
||||
LOG_ERROR << "Error creating texture pixel buffer object.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Creation finished with a success. */
|
||||
texture_.is_created = true;
|
||||
graphics_interop_buffer_.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::texture_update_if_needed()
|
||||
{
|
||||
if (!texture_.need_update) {
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, nullptr);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
texture_.need_update = false;
|
||||
}
|
||||
|
||||
void OpenGLDisplayDriver::vertex_buffer_update(const Params ¶ms)
|
||||
{
|
||||
/* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
|
||||
* rendered. */
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), nullptr, GL_STREAM_DRAW);
|
||||
|
||||
float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
|
||||
if (!vpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
vpointer[0] = 0.0f;
|
||||
vpointer[1] = 0.0f;
|
||||
vpointer[2] = params.full_offset.x;
|
||||
vpointer[3] = params.full_offset.y;
|
||||
|
||||
vpointer[4] = 1.0f;
|
||||
vpointer[5] = 0.0f;
|
||||
vpointer[6] = (float)params.size.x + params.full_offset.x;
|
||||
vpointer[7] = params.full_offset.y;
|
||||
|
||||
vpointer[8] = 1.0f;
|
||||
vpointer[9] = 1.0f;
|
||||
vpointer[10] = (float)params.size.x + params.full_offset.x;
|
||||
vpointer[11] = (float)params.size.y + params.full_offset.y;
|
||||
|
||||
vpointer[12] = 0.0f;
|
||||
vpointer[13] = 1.0f;
|
||||
vpointer[14] = params.full_offset.x;
|
||||
vpointer[15] = (float)params.size.y + params.full_offset.y;
|
||||
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
119
blender-5.2.0/intern/cycles/app/opengl/display_driver.h
Normal file
119
blender-5.2.0/intern/cycles/app/opengl/display_driver.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
#include "app/opengl/shader.h"
|
||||
|
||||
#include "session/display_driver.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class OpenGLDisplayDriver : public DisplayDriver {
|
||||
public:
|
||||
/* Callbacks for enabling and disabling the OpenGL context. Must be provided to support enabling
|
||||
* the context on the Cycles render thread independent of the main thread. */
|
||||
OpenGLDisplayDriver(const std::function<bool()> &gl_context_enable,
|
||||
const std::function<void()> &gl_context_disable);
|
||||
~OpenGLDisplayDriver() override;
|
||||
|
||||
void graphics_interop_activate() override;
|
||||
void graphics_interop_deactivate() override;
|
||||
|
||||
void zero() override;
|
||||
|
||||
void set_zoom(const float zoom_x, const float zoom_y);
|
||||
|
||||
protected:
|
||||
void next_tile_begin() override;
|
||||
|
||||
bool update_begin(const Params ¶ms,
|
||||
const int texture_width,
|
||||
const int texture_height) override;
|
||||
void update_end() override;
|
||||
|
||||
half4 *map_texture_buffer() override;
|
||||
void unmap_texture_buffer() override;
|
||||
|
||||
GraphicsInteropDevice graphics_interop_get_device() override;
|
||||
void graphics_interop_update_buffer() override;
|
||||
|
||||
void draw(const Params ¶ms) override;
|
||||
|
||||
/* Make sure texture is allocated and its initial configuration is performed. */
|
||||
bool gl_texture_resources_ensure();
|
||||
|
||||
/* Ensure all runtime GPU resources needed for drawing are allocated.
|
||||
* Returns true if all resources needed for drawing are available. */
|
||||
bool gl_draw_resources_ensure();
|
||||
|
||||
/* Destroy all GPU resources which are being used by this object. */
|
||||
void gl_resources_destroy();
|
||||
|
||||
/* Update GPU texture dimensions and content if needed (new pixel data was provided).
|
||||
*
|
||||
* NOTE: The texture needs to be bound. */
|
||||
void texture_update_if_needed();
|
||||
|
||||
/* Update vertex buffer with new coordinates of vertex positions and texture coordinates.
|
||||
* This buffer is used to render texture in the viewport.
|
||||
*
|
||||
* NOTE: The buffer needs to be bound. */
|
||||
void vertex_buffer_update(const Params ¶ms);
|
||||
|
||||
/* Texture which contains pixels of the render result. */
|
||||
struct {
|
||||
/* Indicates whether texture creation was attempted and succeeded.
|
||||
* Used to avoid multiple attempts of texture creation on GPU issues or GPU context
|
||||
* misconfiguration. */
|
||||
bool creation_attempted = false;
|
||||
bool is_created = false;
|
||||
|
||||
/* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write
|
||||
* pixels to it.
|
||||
*
|
||||
* NOTE: Allocated on the engine's context. */
|
||||
uint gl_id = 0;
|
||||
uint gl_pbo_id = 0;
|
||||
|
||||
/* Is true when new data was written to the PBO, meaning, the texture might need to be resized
|
||||
* and new data is to be uploaded to the GPU. */
|
||||
bool need_update = false;
|
||||
|
||||
/* Content of the texture is to be filled with zeroes. */
|
||||
std::atomic<bool> need_zero = true;
|
||||
|
||||
/* Dimensions of the texture in pixels. */
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
/* Dimensions of the underlying PBO. */
|
||||
int buffer_width = 0;
|
||||
int buffer_height = 0;
|
||||
} texture_;
|
||||
|
||||
OpenGLShader display_shader_;
|
||||
|
||||
/* Special track of whether GPU resources were attempted to be created, to avoid attempts of
|
||||
* their re-creation on failure on every redraw. */
|
||||
bool gl_draw_resource_creation_attempted_ = false;
|
||||
bool gl_draw_resources_created_ = false;
|
||||
|
||||
/* Vertex buffer which hold vertices of a triangle fan which is textures with the texture
|
||||
* holding the render result. */
|
||||
uint vertex_buffer_ = 0;
|
||||
|
||||
void *gl_render_sync_ = nullptr;
|
||||
void *gl_upload_sync_ = nullptr;
|
||||
|
||||
float2 zoom_ = make_float2(1.0f, 1.0f);
|
||||
|
||||
std::function<bool()> gl_context_enable_ = nullptr;
|
||||
std::function<void()> gl_context_disable_ = nullptr;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
198
blender-5.2.0/intern/cycles/app/opengl/shader.cpp
Normal file
198
blender-5.2.0/intern/cycles/app/opengl/shader.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "app/opengl/shader.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/string.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OpenGLShader.
|
||||
*/
|
||||
|
||||
static const char *VERTEX_SHADER =
|
||||
"#version 330\n"
|
||||
"uniform vec2 fullscreen;\n"
|
||||
"in vec2 texCoord;\n"
|
||||
"in vec2 pos;\n"
|
||||
"out vec2 texCoord_interp;\n"
|
||||
"\n"
|
||||
"vec2 normalize_coordinates()\n"
|
||||
"{\n"
|
||||
" return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n"
|
||||
" texCoord_interp = texCoord;\n"
|
||||
"}\n\0";
|
||||
|
||||
static const char *FRAGMENT_SHADER =
|
||||
"#version 330\n"
|
||||
"uniform sampler2D image_texture;\n"
|
||||
"in vec2 texCoord_interp;\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec4 rgba = texture(image_texture, texCoord_interp);\n"
|
||||
/* Hard-coded Rec.709 gamma, should use OpenColorIO eventually. */
|
||||
" fragColor = pow(rgba, vec4(0.45, 0.45, 0.45, 1.0));\n"
|
||||
"}\n\0";
|
||||
|
||||
static void shader_print_errors(const char *task, const char *log, const char *code)
|
||||
{
|
||||
LOG_ERROR << "Shader: " << task << " error:";
|
||||
LOG_ERROR << "===== shader string ====";
|
||||
|
||||
std::stringstream stream(code);
|
||||
string partial;
|
||||
|
||||
int line = 1;
|
||||
while (getline(stream, partial, '\n')) {
|
||||
if (line < 10) {
|
||||
LOG_ERROR << " " << line << " " << partial;
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << line << " " << partial;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
LOG_ERROR << log;
|
||||
}
|
||||
|
||||
static int compile_shader_program()
|
||||
{
|
||||
const struct Shader {
|
||||
const char *source;
|
||||
const GLenum type;
|
||||
} shaders[2] = {{VERTEX_SHADER, GL_VERTEX_SHADER}, {FRAGMENT_SHADER, GL_FRAGMENT_SHADER}};
|
||||
|
||||
const GLuint program = glCreateProgram();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
const GLuint shader = glCreateShader(shaders[i].type);
|
||||
|
||||
const string source_str = shaders[i].source;
|
||||
const char *c_str = source_str.c_str();
|
||||
|
||||
glShaderSource(shader, 1, &c_str, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint compile_status;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
|
||||
|
||||
if (!compile_status) {
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(shader, sizeof(log), &length, log);
|
||||
shader_print_errors("compile", log, c_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
/* Link output. */
|
||||
glBindFragDataLocation(program, 0, "fragColor");
|
||||
|
||||
/* Link and error check. */
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint link_status;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
|
||||
if (!link_status) {
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(program, sizeof(log), &length, log);
|
||||
shader_print_errors("linking", log, VERTEX_SHADER);
|
||||
shader_print_errors("linking", log, FRAGMENT_SHADER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
int OpenGLShader::get_position_attrib_location()
|
||||
{
|
||||
if (position_attribute_location_ == -1) {
|
||||
const uint shader_program = get_shader_program();
|
||||
position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name);
|
||||
}
|
||||
return position_attribute_location_;
|
||||
}
|
||||
|
||||
int OpenGLShader::get_tex_coord_attrib_location()
|
||||
{
|
||||
if (tex_coord_attribute_location_ == -1) {
|
||||
const uint shader_program = get_shader_program();
|
||||
tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name);
|
||||
}
|
||||
return tex_coord_attribute_location_;
|
||||
}
|
||||
|
||||
void OpenGLShader::bind(const int width, const int height)
|
||||
{
|
||||
create_shader_if_needed();
|
||||
|
||||
if (!shader_program_) {
|
||||
return;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program_);
|
||||
glUniform1i(image_texture_location_, 0);
|
||||
glUniform2f(fullscreen_location_, width, height);
|
||||
}
|
||||
|
||||
void OpenGLShader::unbind() {}
|
||||
|
||||
uint OpenGLShader::get_shader_program()
|
||||
{
|
||||
return shader_program_;
|
||||
}
|
||||
|
||||
void OpenGLShader::create_shader_if_needed()
|
||||
{
|
||||
if (shader_program_ || shader_compile_attempted_) {
|
||||
return;
|
||||
}
|
||||
|
||||
shader_compile_attempted_ = true;
|
||||
|
||||
shader_program_ = compile_shader_program();
|
||||
if (!shader_program_) {
|
||||
return;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program_);
|
||||
|
||||
image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture");
|
||||
if (image_texture_location_ < 0) {
|
||||
LOG_ERROR << "Shader doesn't contain the 'image_texture' uniform.";
|
||||
destroy_shader();
|
||||
return;
|
||||
}
|
||||
|
||||
fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen");
|
||||
if (fullscreen_location_ < 0) {
|
||||
LOG_ERROR << "Shader doesn't contain the 'fullscreen' uniform.";
|
||||
destroy_shader();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLShader::destroy_shader()
|
||||
{
|
||||
glDeleteProgram(shader_program_);
|
||||
shader_program_ = 0;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
46
blender-5.2.0/intern/cycles/app/opengl/shader.h
Normal file
46
blender-5.2.0/intern/cycles/app/opengl/shader.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 OpenGL Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class OpenGLShader {
|
||||
public:
|
||||
static constexpr const char *position_attribute_name = "pos";
|
||||
static constexpr const char *tex_coord_attribute_name = "texCoord";
|
||||
|
||||
OpenGLShader() = default;
|
||||
virtual ~OpenGLShader() = default;
|
||||
|
||||
/* Get attribute location for position and texture coordinate respectively.
|
||||
* NOTE: The shader needs to be bound to have access to those. */
|
||||
int get_position_attrib_location();
|
||||
int get_tex_coord_attrib_location();
|
||||
|
||||
void bind(const int width, const int height);
|
||||
void unbind();
|
||||
|
||||
protected:
|
||||
uint get_shader_program();
|
||||
|
||||
void create_shader_if_needed();
|
||||
void destroy_shader();
|
||||
|
||||
/* Cached values of various OpenGL resources. */
|
||||
int position_attribute_location_ = -1;
|
||||
int tex_coord_attribute_location_ = -1;
|
||||
|
||||
uint shader_program_ = 0;
|
||||
int image_texture_location_ = -1;
|
||||
int fullscreen_location_ = -1;
|
||||
|
||||
/* Shader compilation attempted. Which means, that if the shader program is 0 then compilation or
|
||||
* linking has failed. Do not attempt to re-compile the shader. */
|
||||
bool shader_compile_attempted_ = false;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
350
blender-5.2.0/intern/cycles/app/opengl/window.cpp
Normal file
350
blender-5.2.0/intern/cycles/app/opengl/window.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "app/opengl/window.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/string.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/version.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* structs */
|
||||
|
||||
struct Window {
|
||||
WindowInitFunc initf = nullptr;
|
||||
WindowExitFunc exitf = nullptr;
|
||||
WindowResizeFunc resize = nullptr;
|
||||
WindowDisplayFunc display = nullptr;
|
||||
WindowKeyboardFunc keyboard = nullptr;
|
||||
WindowMotionFunc motion = nullptr;
|
||||
|
||||
bool first_display = true;
|
||||
bool redraw = false;
|
||||
|
||||
int mouseX = 0, mouseY = 0;
|
||||
int mouseBut0 = 0, mouseBut2 = 0;
|
||||
|
||||
int width = 0, height = 0;
|
||||
|
||||
SDL_Window *window = nullptr;
|
||||
SDL_GLContext gl_context = nullptr;
|
||||
thread_mutex gl_context_mutex;
|
||||
} V;
|
||||
|
||||
/* public */
|
||||
|
||||
static void window_display_text(int /*x*/, int /*y*/, const char *text)
|
||||
{
|
||||
/* Not currently supported, need to add text rendering support. */
|
||||
#if 0
|
||||
const char *c;
|
||||
|
||||
glRasterPos3f(x, y, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
printf("display %s\n", text);
|
||||
|
||||
for (c = text; *c != '\0'; c++) {
|
||||
const uint8_t *bitmap = helvetica10_character_map[*c];
|
||||
glBitmap(bitmap[0],
|
||||
helvetica10_height,
|
||||
helvetica10_x_offset,
|
||||
helvetica10_y_offset,
|
||||
bitmap[0],
|
||||
0.0f,
|
||||
bitmap + 1);
|
||||
}
|
||||
#else
|
||||
static string last_text;
|
||||
|
||||
if (text != last_text) {
|
||||
LOG_INFO_IMPORTANT << text;
|
||||
last_text = text;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void window_display_info(const char *info)
|
||||
{
|
||||
const int height = 20;
|
||||
|
||||
#if 0
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.1f, 0.1f, 0.1f, 0.8f);
|
||||
glRectf(0.0f, V.height - height, V.width, V.height);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glColor3f(0.5f, 0.5f, 0.5f);
|
||||
#endif
|
||||
|
||||
window_display_text(10, 7 + V.height - height, info);
|
||||
|
||||
#if 0
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void window_display_help()
|
||||
{
|
||||
const int w = (int)((float)V.width / 1.15f);
|
||||
const int h = (int)((float)V.height / 1.15f);
|
||||
|
||||
const int x1 = (V.width - w) / 2;
|
||||
#if 0
|
||||
const int x2 = x1 + w;
|
||||
#endif
|
||||
|
||||
const int y1 = (V.height - h) / 2;
|
||||
const int y2 = y1 + h;
|
||||
|
||||
#if 0
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.5f, 0.5f, 0.5f, 0.8f);
|
||||
glRectf(x1, y1, x2, y2);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glColor3f(0.8f, 0.8f, 0.8f);
|
||||
#endif
|
||||
|
||||
const string info = string("Cycles Renderer ") + CYCLES_VERSION_STRING;
|
||||
|
||||
window_display_text(x1 + 20, y2 - 20, info.c_str());
|
||||
window_display_text(x1 + 20, y2 - 40, "(C) 2011-2016 Blender Foundation");
|
||||
window_display_text(x1 + 20, y2 - 80, "Controls:");
|
||||
window_display_text(x1 + 20, y2 - 100, "h: Info/Help");
|
||||
window_display_text(x1 + 20, y2 - 120, "r: Reset");
|
||||
window_display_text(x1 + 20, y2 - 140, "p: Pause");
|
||||
window_display_text(x1 + 20, y2 - 160, "esc: Cancel");
|
||||
window_display_text(x1 + 20, y2 - 180, "q: Quit program");
|
||||
|
||||
window_display_text(x1 + 20, y2 - 210, "i: Interactive mode");
|
||||
window_display_text(x1 + 20, y2 - 230, "Left mouse: Move camera");
|
||||
window_display_text(x1 + 20, y2 - 250, "Right mouse: Rotate camera");
|
||||
window_display_text(x1 + 20, y2 - 270, "W/A/S/D: Move camera");
|
||||
window_display_text(x1 + 20, y2 - 290, "0/1/2/3: Set max bounces");
|
||||
|
||||
#if 0
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void window_display()
|
||||
{
|
||||
if (V.first_display) {
|
||||
if (V.initf) {
|
||||
V.initf();
|
||||
}
|
||||
if (V.exitf) {
|
||||
atexit(V.exitf);
|
||||
}
|
||||
|
||||
V.first_display = false;
|
||||
}
|
||||
|
||||
window_opengl_context_enable();
|
||||
|
||||
glViewport(0, 0, V.width, V.height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glClearColor(0.05f, 0.05f, 0.05f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, V.width, 0, V.height, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glRasterPos3f(0, 0, 0);
|
||||
|
||||
if (V.display) {
|
||||
V.display();
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(V.window);
|
||||
window_opengl_context_disable();
|
||||
}
|
||||
|
||||
static void window_reshape(const int width, const int height)
|
||||
{
|
||||
if (V.width != width || V.height != height) {
|
||||
if (V.resize) {
|
||||
V.resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
V.width = width;
|
||||
V.height = height;
|
||||
}
|
||||
|
||||
static bool window_keyboard(unsigned char key)
|
||||
{
|
||||
if (V.keyboard) {
|
||||
V.keyboard(key);
|
||||
}
|
||||
|
||||
if (key == 'q') {
|
||||
if (V.exitf) {
|
||||
V.exitf();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void window_mouse(const int button, const int state, const int x, int y)
|
||||
{
|
||||
if (button == SDL_BUTTON_LEFT) {
|
||||
if (state == SDL_EVENT_MOUSE_BUTTON_DOWN) {
|
||||
V.mouseX = x;
|
||||
V.mouseY = y;
|
||||
V.mouseBut0 = 1;
|
||||
}
|
||||
else if (state == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||
V.mouseBut0 = 0;
|
||||
}
|
||||
}
|
||||
else if (button == SDL_BUTTON_RIGHT) {
|
||||
if (state == SDL_EVENT_MOUSE_BUTTON_DOWN) {
|
||||
V.mouseX = x;
|
||||
V.mouseY = y;
|
||||
V.mouseBut2 = 1;
|
||||
}
|
||||
else if (state == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||
V.mouseBut2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void window_motion(const int x, const int y)
|
||||
{
|
||||
const int but = V.mouseBut0 ? 0 : 2;
|
||||
const int distX = x - V.mouseX;
|
||||
const int distY = y - V.mouseY;
|
||||
|
||||
if (V.motion) {
|
||||
V.motion(distX, distY, but);
|
||||
}
|
||||
|
||||
V.mouseX = x;
|
||||
V.mouseY = y;
|
||||
}
|
||||
|
||||
bool window_opengl_context_enable()
|
||||
{
|
||||
V.gl_context_mutex.lock();
|
||||
SDL_GL_MakeCurrent(V.window, V.gl_context);
|
||||
return true;
|
||||
}
|
||||
|
||||
void window_opengl_context_disable()
|
||||
{
|
||||
SDL_GL_MakeCurrent(V.window, nullptr);
|
||||
V.gl_context_mutex.unlock();
|
||||
}
|
||||
|
||||
void window_main_loop(const char *title,
|
||||
const int width,
|
||||
const int height,
|
||||
WindowInitFunc initf,
|
||||
WindowExitFunc exitf,
|
||||
WindowResizeFunc resize,
|
||||
WindowDisplayFunc display,
|
||||
WindowKeyboardFunc keyboard,
|
||||
WindowMotionFunc motion)
|
||||
{
|
||||
V.width = width;
|
||||
V.height = height;
|
||||
V.first_display = true;
|
||||
V.redraw = false;
|
||||
V.initf = initf;
|
||||
V.exitf = exitf;
|
||||
V.resize = resize;
|
||||
V.display = display;
|
||||
V.keyboard = keyboard;
|
||||
V.motion = motion;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
V.window = SDL_CreateWindow(title, width, height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
|
||||
if (V.window == nullptr) {
|
||||
LOG_ERROR << "Failed to create window: " << SDL_GetError();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_RaiseWindow(V.window);
|
||||
|
||||
V.gl_context = SDL_GL_CreateContext(V.window);
|
||||
SDL_GL_MakeCurrent(V.window, nullptr);
|
||||
|
||||
window_reshape(width, height);
|
||||
window_display();
|
||||
|
||||
while (true) {
|
||||
bool quit = false;
|
||||
SDL_Event event;
|
||||
while (!quit && SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key < 128) {
|
||||
quit = window_keyboard(char(event.key.key));
|
||||
}
|
||||
else if (event.type == SDL_EVENT_MOUSE_MOTION) {
|
||||
window_motion(int(event.motion.x), int(event.motion.y));
|
||||
}
|
||||
else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
{
|
||||
window_mouse(event.button.button, event.type, int(event.button.x), int(event.button.y));
|
||||
}
|
||||
else if (event.type == SDL_EVENT_WINDOW_RESIZED) {
|
||||
window_reshape(event.window.data1, event.window.data2);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_QUIT) {
|
||||
if (V.exitf) {
|
||||
V.exitf();
|
||||
}
|
||||
quit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (V.redraw) {
|
||||
V.redraw = false;
|
||||
window_display();
|
||||
}
|
||||
|
||||
SDL_WaitEventTimeout(nullptr, 100);
|
||||
}
|
||||
|
||||
SDL_GL_DestroyContext(V.gl_context);
|
||||
SDL_DestroyWindow(V.window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void window_redraw()
|
||||
{
|
||||
V.redraw = true;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
36
blender-5.2.0/intern/cycles/app/opengl/window.h
Normal file
36
blender-5.2.0/intern/cycles/app/opengl/window.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Functions to display a simple OpenGL window using SDL, simplified to the
|
||||
* bare minimum we need to reduce boilerplate code in tests apps. */
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
using WindowInitFunc = void (*)();
|
||||
using WindowExitFunc = void (*)();
|
||||
using WindowResizeFunc = void (*)(int, int);
|
||||
using WindowDisplayFunc = void (*)();
|
||||
using WindowKeyboardFunc = void (*)(unsigned char);
|
||||
using WindowMotionFunc = void (*)(int, int, int);
|
||||
|
||||
void window_main_loop(const char *title,
|
||||
const int width,
|
||||
const int height,
|
||||
WindowInitFunc initf,
|
||||
WindowExitFunc exitf,
|
||||
WindowResizeFunc resize,
|
||||
WindowDisplayFunc display,
|
||||
WindowKeyboardFunc keyboard,
|
||||
WindowMotionFunc motion);
|
||||
|
||||
void window_display_info(const char *info);
|
||||
void window_display_help();
|
||||
void window_redraw();
|
||||
|
||||
bool window_opengl_context_enable();
|
||||
void window_opengl_context_disable();
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
30
blender-5.2.0/intern/cycles/blender/CCL_api.h
Normal file
30
blender-5.2.0/intern/cycles/blender/CCL_api.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Image;
|
||||
|
||||
/* Create python module _cycles used by addon. */
|
||||
void *CCL_python_module_init();
|
||||
|
||||
void CCL_log_init();
|
||||
void CCL_implicit_sharing_init();
|
||||
|
||||
/* Texture cache generation. */
|
||||
|
||||
bool CCL_resolve_texture_cache(const Image *image,
|
||||
const char *filepath,
|
||||
const char *texture_cache_directory,
|
||||
std::string &r_tx_filepath);
|
||||
|
||||
bool CCL_generate_texture_cache(const Image *image,
|
||||
const char *filepath,
|
||||
const char *texture_cache_directory = "");
|
||||
|
||||
} // namespace blender
|
||||
113
blender-5.2.0/intern/cycles/blender/CMakeLists.txt
Normal file
113
blender-5.2.0/intern/cycles/blender/CMakeLists.txt
Normal file
@@ -0,0 +1,113 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
..
|
||||
../../../source/blender/makesrna
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
camera.cpp
|
||||
device.cpp
|
||||
display_driver.cpp
|
||||
image.cpp
|
||||
implicit_sharing.cpp
|
||||
geometry.cpp
|
||||
light.cpp
|
||||
light_linking.cpp
|
||||
mesh.cpp
|
||||
object.cpp
|
||||
object_cull.cpp
|
||||
output_driver.cpp
|
||||
particles.cpp
|
||||
pointcloud.cpp
|
||||
curves.cpp
|
||||
logging.cpp
|
||||
python.cpp
|
||||
session.cpp
|
||||
shader.cpp
|
||||
sync.cpp
|
||||
texture_cache.cpp
|
||||
viewport.cpp
|
||||
volume.cpp
|
||||
|
||||
attribute_convert.h
|
||||
CCL_api.h
|
||||
device.h
|
||||
display_driver.h
|
||||
id_map.h
|
||||
image.h
|
||||
light_linking.h
|
||||
object_cull.h
|
||||
output_driver.h
|
||||
sync.h
|
||||
session.h
|
||||
util.h
|
||||
viewport.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::geometry
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::nodes::shader
|
||||
PRIVATE bf::render
|
||||
PRIVATE cycles_bvh
|
||||
PRIVATE cycles_device
|
||||
PRIVATE cycles_graph
|
||||
PRIVATE cycles_kernel
|
||||
PRIVATE cycles_scene
|
||||
PRIVATE cycles_session
|
||||
PRIVATE cycles_subd
|
||||
PRIVATE cycles_util
|
||||
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
PRIVATE bf::dependencies::optional::openimagedenoise
|
||||
PRIVATE bf::dependencies::optional::openvdb
|
||||
PRIVATE bf::dependencies::optional::osl
|
||||
)
|
||||
|
||||
set(ADDON_FILES
|
||||
addon/__init__.py
|
||||
addon/camera.py
|
||||
addon/engine.py
|
||||
addon/maketx.py
|
||||
addon/operators.py
|
||||
addon/osl.py
|
||||
addon/presets.py
|
||||
addon/properties.py
|
||||
addon/ui.py
|
||||
addon/version_update.py
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_DEVICE_HIP)
|
||||
add_definitions(-DWITH_HIP)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEVICE_METAL)
|
||||
add_definitions(-DWITH_METAL)
|
||||
endif()
|
||||
|
||||
if(WITH_MOD_FLUID)
|
||||
add_definitions(-DWITH_FLUID)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
add_dependencies(bf_intern_cycles bf_rna)
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${ADDON_FILES}" ${CYCLES_INSTALL_PATH})
|
||||
185
blender-5.2.0/intern/cycles/blender/addon/__init__.py
Normal file
185
blender-5.2.0/intern/cycles/blender/addon/__init__.py
Normal file
@@ -0,0 +1,185 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
bl_info = {
|
||||
"name": "Cycles Render Engine",
|
||||
"author": "",
|
||||
"blender": (2, 80, 0),
|
||||
"description": "Cycles renderer integration",
|
||||
"warning": "",
|
||||
"doc_url": "https://docs.blender.org/manual/en/latest/render/cycles/",
|
||||
"tracker_url": "",
|
||||
"support": 'OFFICIAL',
|
||||
"category": "Render"}
|
||||
|
||||
# Support 'reload' case.
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
if "engine" in locals():
|
||||
importlib.reload(engine)
|
||||
if "version_update" in locals():
|
||||
importlib.reload(version_update)
|
||||
if "ui" in locals():
|
||||
importlib.reload(ui)
|
||||
if "operators" in locals():
|
||||
importlib.reload(operators)
|
||||
if "properties" in locals():
|
||||
importlib.reload(properties)
|
||||
if "presets" in locals():
|
||||
importlib.reload(presets)
|
||||
if "maketx" in locals():
|
||||
importlib.reload(maketx)
|
||||
|
||||
import bpy
|
||||
|
||||
from . import (
|
||||
engine,
|
||||
version_update,
|
||||
)
|
||||
|
||||
|
||||
class CyclesRender(bpy.types.RenderEngine):
|
||||
bl_idname = 'CYCLES'
|
||||
bl_label = "Cycles"
|
||||
bl_use_eevee_viewport = True
|
||||
bl_use_preview = True
|
||||
bl_use_exclude_layers = True
|
||||
bl_use_spherical_stereo = True
|
||||
bl_use_custom_freestyle = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.session = None
|
||||
|
||||
def __del__(self):
|
||||
engine.free(self)
|
||||
|
||||
# final render
|
||||
def update(self, data, depsgraph):
|
||||
if not self.session:
|
||||
if self.is_preview:
|
||||
cscene = bpy.context.scene.cycles
|
||||
use_osl = cscene.shading_system
|
||||
|
||||
engine.create(self, data, preview_osl=use_osl)
|
||||
else:
|
||||
engine.create(self, data)
|
||||
|
||||
engine.reset(self, data, depsgraph)
|
||||
|
||||
def render(self, depsgraph):
|
||||
engine.render(self, depsgraph)
|
||||
|
||||
def render_frame_finish(self):
|
||||
engine.render_frame_finish(self)
|
||||
|
||||
def draw(self, context, depsgraph):
|
||||
engine.draw(self, depsgraph, context.space_data)
|
||||
|
||||
def bake(self, depsgraph, obj, pass_type, pass_filter, width, height):
|
||||
engine.bake(self, depsgraph, obj, pass_type, pass_filter, width, height)
|
||||
|
||||
# viewport render
|
||||
def view_update(self, context, depsgraph):
|
||||
if not self.session:
|
||||
# When starting a new render session in viewport (by switching
|
||||
# viewport to Rendered shading) unpause the render. The way to think
|
||||
# of it is: artist requests render, so we start to render.
|
||||
# Do it for both original and evaluated scene so that Cycles
|
||||
# immediately reacts to un-paused render.
|
||||
cscene = context.scene.cycles
|
||||
cscene_eval = depsgraph.scene_eval.cycles
|
||||
if cscene.preview_pause or cscene_eval.preview_pause:
|
||||
cscene.preview_pause = False
|
||||
cscene_eval.preview_pause = False
|
||||
|
||||
engine.create(self, context.blend_data,
|
||||
context.region, context.space_data, context.region_data)
|
||||
|
||||
engine.reset(self, context.blend_data, depsgraph)
|
||||
engine.sync(self, depsgraph, context.blend_data)
|
||||
|
||||
def view_draw(self, context, depsgraph):
|
||||
engine.view_draw(self, depsgraph, context.region, context.space_data, context.region_data)
|
||||
|
||||
def update_script_node(self, node):
|
||||
if engine.with_osl():
|
||||
from . import osl
|
||||
osl.update_script_node(node, self.report)
|
||||
else:
|
||||
self.report({'ERROR'}, "OSL support disabled in this build")
|
||||
|
||||
def update_custom_camera(self, cam):
|
||||
if engine.with_osl():
|
||||
from . import osl
|
||||
osl.update_custom_camera_shader(cam, self.report)
|
||||
else:
|
||||
self.report({'ERROR'}, "OSL support disabled in this build")
|
||||
|
||||
def update_render_passes(self, scene, srl):
|
||||
engine.register_passes(self, scene, srl)
|
||||
|
||||
|
||||
def engine_exit():
|
||||
engine.exit()
|
||||
|
||||
|
||||
classes = (
|
||||
CyclesRender,
|
||||
)
|
||||
|
||||
|
||||
cli_commands = []
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
from . import ui
|
||||
from . import operators
|
||||
from . import properties
|
||||
from . import presets
|
||||
from .maketx import maketx_command
|
||||
import atexit
|
||||
|
||||
# Make sure we only registered the callback once.
|
||||
atexit.unregister(engine_exit)
|
||||
atexit.register(engine_exit)
|
||||
|
||||
engine.init()
|
||||
|
||||
properties.register()
|
||||
ui.register()
|
||||
operators.register()
|
||||
presets.register()
|
||||
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
bpy.app.handlers.version_update.append(version_update.do_versions)
|
||||
|
||||
cli_commands.append(bpy.utils.register_cli_command("maketx", maketx_command))
|
||||
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
from . import ui
|
||||
from . import operators
|
||||
from . import properties
|
||||
from . import presets
|
||||
|
||||
bpy.app.handlers.version_update.remove(version_update.do_versions)
|
||||
|
||||
for cmd in cli_commands:
|
||||
bpy.utils.unregister_cli_command(cmd)
|
||||
cli_commands.clear()
|
||||
|
||||
ui.unregister()
|
||||
operators.unregister()
|
||||
properties.unregister()
|
||||
presets.unregister()
|
||||
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
78
blender-5.2.0/intern/cycles/blender/addon/camera.py
Normal file
78
blender-5.2.0/intern/cycles/blender/addon/camera.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Fit to match default projective camera with focal_length 50 and sensor_width 36.
|
||||
default_fisheye_polynomial = [
|
||||
-1.1735143712967577e-05,
|
||||
-0.019988736953434998,
|
||||
-3.3525322965709175e-06,
|
||||
3.099275275886036e-06,
|
||||
-2.6064646454854524e-08,
|
||||
]
|
||||
|
||||
|
||||
# Utilities to generate lens polynomials to match built-in camera types, only here
|
||||
# for reference at the moment, not used by the code.
|
||||
def create_grid(sensor_height, sensor_width):
|
||||
import numpy as np
|
||||
if sensor_height is None:
|
||||
sensor_height = sensor_width / (16 / 9) # Default aspect ration 16:9
|
||||
uu, vv = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
|
||||
uu = (uu - 0.5) * sensor_width
|
||||
vv = (vv - 0.5) * sensor_height
|
||||
rr = np.sqrt(uu ** 2 + vv ** 2)
|
||||
return rr
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_projective(focal_length=50, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length)).flat, 4)
|
||||
return list(reversed(polynomial))
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_projective_fov(fov, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
f = sensor_width / 2 / np.tan(fov / 2)
|
||||
return fisheye_lens_polynomial_from_projective(f, sensor_width, sensor_height)
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_equisolid(lens=10.5, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
x = rr.reshape(-1)
|
||||
x = np.stack([x**i for i in [1, 2, 3, 4]])
|
||||
y = (-2 * np.arcsin(rr / (2 * lens))).reshape(-1)
|
||||
polynomial = np.linalg.lstsq(x.T, y.T, rcond=None)[0]
|
||||
return [0] + list(polynomial)
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_equidistant(fov=180, sensor_width=36, sensor_height=None):
|
||||
import numpy as np
|
||||
return [0, -np.radians(fov) / sensor_width, 0, 0, 0]
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_distorted_projective_polynomial(
|
||||
k1, k2, k3, focal_length=50, sensor_width=36, sensor_height=None,
|
||||
):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
r2 = (rr / focal_length) ** 2
|
||||
r4 = r2 * r2
|
||||
r6 = r4 * r2
|
||||
r_coeff = 1 + k1 * r2 + k2 * r4 + k3 * r6
|
||||
polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length * r_coeff)).flat, 4)
|
||||
return list(reversed(polynomial))
|
||||
|
||||
|
||||
def fisheye_lens_polynomial_from_distorted_projective_divisions(
|
||||
k1, k2, focal_length=50, sensor_width=36, sensor_height=None,
|
||||
):
|
||||
import numpy as np
|
||||
rr = create_grid(sensor_height, sensor_width)
|
||||
r2 = (rr / focal_length) ** 2
|
||||
r4 = r2 * r2
|
||||
r_coeff = 1 + k1 * r2 + k2 * r4
|
||||
polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length / r_coeff)).flat, 4)
|
||||
return list(reversed(polynomial))
|
||||
277
blender-5.2.0/intern/cycles/blender/addon/engine.py
Normal file
277
blender-5.2.0/intern/cycles/blender/addon/engine.py
Normal file
@@ -0,0 +1,277 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def _configure_argument_parser():
|
||||
import argparse
|
||||
# No help because it conflicts with general Python scripts argument parsing
|
||||
parser = argparse.ArgumentParser(description="Cycles Addon argument parser",
|
||||
add_help=False)
|
||||
parser.add_argument("--cycles-print-stats",
|
||||
help="Print rendering statistics to stderr",
|
||||
action='store_true')
|
||||
parser.add_argument("--cycles-device",
|
||||
help="Set the device to use for Cycles, overriding user preferences and the scene setting."
|
||||
"Valid options are 'CPU', 'CUDA', 'OPTIX', 'HIP', 'ONEAPI', or 'METAL'."
|
||||
"Additionally, you can append '+CPU' to any GPU type for hybrid rendering.",
|
||||
default=None)
|
||||
return parser
|
||||
|
||||
|
||||
def _parse_command_line():
|
||||
import sys
|
||||
|
||||
argv = sys.argv
|
||||
if "--" not in argv:
|
||||
return
|
||||
|
||||
parser = _configure_argument_parser()
|
||||
args, _ = parser.parse_known_args(argv[argv.index("--") + 1:])
|
||||
|
||||
if args.cycles_print_stats:
|
||||
import _cycles
|
||||
_cycles.enable_print_stats()
|
||||
|
||||
if args.cycles_device:
|
||||
import _cycles
|
||||
if not _cycles.set_device_override(args.cycles_device):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def init():
|
||||
import bpy
|
||||
import _cycles
|
||||
import os.path
|
||||
|
||||
path = os.path.dirname(__file__)
|
||||
user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', path='')))
|
||||
|
||||
_cycles.init(path, user_path, bpy.app.background)
|
||||
_parse_command_line()
|
||||
|
||||
|
||||
def exit():
|
||||
import _cycles
|
||||
_cycles.exit()
|
||||
|
||||
|
||||
def create(engine, data, region=None, v3d=None, rv3d=None, preview_osl=False):
|
||||
import _cycles
|
||||
import bpy
|
||||
|
||||
data = data.as_pointer()
|
||||
prefs = bpy.context.preferences.as_pointer()
|
||||
screen = 0
|
||||
if region:
|
||||
screen = region.id_data.as_pointer()
|
||||
region = region.as_pointer()
|
||||
if v3d:
|
||||
screen = screen or v3d.id_data.as_pointer()
|
||||
v3d = v3d.as_pointer()
|
||||
if rv3d:
|
||||
screen = screen or rv3d.id_data.as_pointer()
|
||||
rv3d = rv3d.as_pointer()
|
||||
|
||||
engine.session = _cycles.create(engine.as_pointer(), prefs, data, screen, region, v3d, rv3d, preview_osl)
|
||||
|
||||
|
||||
def free(engine):
|
||||
if hasattr(engine, "session"):
|
||||
if engine.session:
|
||||
import _cycles
|
||||
_cycles.free(engine.session)
|
||||
del engine.session
|
||||
|
||||
|
||||
def render(engine, depsgraph):
|
||||
import _cycles
|
||||
if hasattr(engine, "session"):
|
||||
_cycles.render(engine.session, depsgraph.as_pointer())
|
||||
|
||||
|
||||
def render_frame_finish(engine):
|
||||
if not engine.session:
|
||||
return
|
||||
|
||||
import _cycles
|
||||
_cycles.render_frame_finish(engine.session)
|
||||
|
||||
|
||||
def draw(engine, depsgraph, space_image):
|
||||
if not engine.session:
|
||||
return
|
||||
|
||||
depsgraph_ptr = depsgraph.as_pointer()
|
||||
space_image_ptr = space_image.as_pointer()
|
||||
screen_ptr = space_image.id_data.as_pointer()
|
||||
|
||||
import _cycles
|
||||
_cycles.draw(engine.session, depsgraph_ptr, screen_ptr, space_image_ptr)
|
||||
|
||||
|
||||
def bake(engine, depsgraph, obj, pass_type, pass_filter, width, height):
|
||||
import _cycles
|
||||
session = getattr(engine, "session", None)
|
||||
if session is not None:
|
||||
_cycles.bake(engine.session, depsgraph.as_pointer(), obj.as_pointer(), pass_type, pass_filter, width, height)
|
||||
|
||||
|
||||
def reset(engine, data, depsgraph):
|
||||
import _cycles
|
||||
import bpy
|
||||
|
||||
prefs = bpy.context.preferences
|
||||
if prefs.experimental.use_cycles_debug and prefs.view.show_developer_ui:
|
||||
_cycles.debug_flags_update(depsgraph.scene.as_pointer())
|
||||
else:
|
||||
_cycles.debug_flags_reset()
|
||||
|
||||
data = data.as_pointer()
|
||||
depsgraph = depsgraph.as_pointer()
|
||||
_cycles.reset(engine.session, data, depsgraph)
|
||||
|
||||
|
||||
def sync(engine, depsgraph, data):
|
||||
import _cycles
|
||||
_cycles.sync(engine.session, depsgraph.as_pointer())
|
||||
|
||||
|
||||
def view_draw(engine, depsgraph, region, v3d, rv3d):
|
||||
import _cycles
|
||||
depsgraph = depsgraph.as_pointer()
|
||||
v3d = v3d.as_pointer()
|
||||
rv3d = rv3d.as_pointer()
|
||||
|
||||
# draw render image
|
||||
_cycles.view_draw(engine.session, depsgraph, v3d, rv3d)
|
||||
|
||||
|
||||
def available_devices():
|
||||
import _cycles
|
||||
return _cycles.available_devices()
|
||||
|
||||
|
||||
def with_osl():
|
||||
import _cycles
|
||||
return _cycles.with_osl
|
||||
|
||||
|
||||
def osl_version():
|
||||
import _cycles
|
||||
return _cycles.osl_version
|
||||
|
||||
|
||||
def with_path_guiding():
|
||||
import _cycles
|
||||
return _cycles.with_path_guiding
|
||||
|
||||
|
||||
def system_info():
|
||||
import _cycles
|
||||
return _cycles.system_info()
|
||||
|
||||
|
||||
def list_render_passes(scene, srl):
|
||||
import _cycles
|
||||
from bpy.app.translations import pgettext_n as n_
|
||||
|
||||
crl = srl.cycles
|
||||
|
||||
# Combined pass.
|
||||
yield ("Combined", "RGBA", 'COLOR')
|
||||
|
||||
# Keep alignment for readability.
|
||||
# autopep8: off
|
||||
|
||||
# Data passes.
|
||||
if srl.use_pass_z: yield (n_("Depth"), "Z", 'VALUE')
|
||||
if srl.use_pass_mist: yield (n_("Mist"), "Z", 'VALUE')
|
||||
if srl.use_pass_position: yield (n_("Position"), "XYZ", 'VECTOR')
|
||||
if srl.use_pass_normal: yield (n_("Normal"), "XYZ", 'VECTOR')
|
||||
if srl.use_pass_vector: yield (n_("Vector"), "XYZW", 'VECTOR')
|
||||
if srl.use_pass_uv: yield (n_("UV"), "UVA", 'VECTOR')
|
||||
if srl.use_pass_object_index: yield (n_("Object Index"), "X", 'VALUE')
|
||||
if srl.use_pass_material_index: yield (n_("Material Index"), "X", 'VALUE')
|
||||
|
||||
# Light passes.
|
||||
if srl.use_pass_diffuse_direct: yield (n_("Diffuse Direct"), "RGB", 'COLOR')
|
||||
if srl.use_pass_diffuse_indirect: yield (n_("Diffuse Indirect"), "RGB", 'COLOR')
|
||||
if srl.use_pass_diffuse_color: yield (n_("Diffuse Color"), "RGB", 'COLOR')
|
||||
if srl.use_pass_glossy_direct: yield (n_("Glossy Direct"), "RGB", 'COLOR')
|
||||
if srl.use_pass_glossy_indirect: yield (n_("Glossy Indirect"), "RGB", 'COLOR')
|
||||
if srl.use_pass_glossy_color: yield (n_("Glossy Color"), "RGB", 'COLOR')
|
||||
if srl.use_pass_transmission_direct: yield (n_("Transmission Direct"), "RGB", 'COLOR')
|
||||
if srl.use_pass_transmission_indirect: yield (n_("Transmission Indirect"), "RGB", 'COLOR')
|
||||
if srl.use_pass_transmission_color: yield (n_("Transmission Color"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_direct: yield (n_("Volume Direct"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_indirect: yield (n_("Volume Indirect"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_scatter: yield (n_("Volume Scatter"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_transmit: yield (n_("Volume Transmit"), "RGB", 'COLOR')
|
||||
if crl.use_pass_volume_majorant: yield (n_("Volume Majorant"), "Z", 'VALUE')
|
||||
if srl.use_pass_emit: yield (n_("Emission"), "RGB", 'COLOR')
|
||||
if srl.use_pass_environment: yield (n_("Environment"), "RGB", 'COLOR')
|
||||
if srl.use_pass_ambient_occlusion: yield (n_("Ambient Occlusion"), "RGB", 'COLOR')
|
||||
if crl.use_pass_shadow_catcher: yield (n_("Shadow Catcher"), "RGB", 'COLOR')
|
||||
# autopep8: on
|
||||
|
||||
# Debug passes.
|
||||
if crl.pass_debug_sample_count:
|
||||
yield (n_("Debug Sample Count"), "X", 'VALUE')
|
||||
if crl.pass_render_time:
|
||||
# Only yield the pass if rendering on CPU
|
||||
if scene.cycles.device == 'CPU':
|
||||
yield (n_("Render Time"), "X", "VALUE")
|
||||
|
||||
# Cryptomatte passes.
|
||||
# NOTE: Name channels are lowercase RGBA so that compression rules check in OpenEXR DWA code
|
||||
# uses lossless compression. Reportedly this naming is the only one which works good from the
|
||||
# interoperability point of view. Using XYZW naming is not portable.
|
||||
crypto_depth = (min(16, srl.pass_cryptomatte_depth) + 1) // 2
|
||||
if srl.use_pass_cryptomatte_object:
|
||||
for i in range(0, crypto_depth):
|
||||
yield ("CryptoObject" + '{:02d}'.format(i), "rgba", 'COLOR')
|
||||
if srl.use_pass_cryptomatte_material:
|
||||
for i in range(0, crypto_depth):
|
||||
yield ("CryptoMaterial" + '{:02d}'.format(i), "rgba", 'COLOR')
|
||||
if srl.use_pass_cryptomatte_asset:
|
||||
for i in range(0, crypto_depth):
|
||||
yield ("CryptoAsset" + '{:02d}'.format(i), "rgba", 'COLOR')
|
||||
|
||||
# Denoising passes.
|
||||
if scene.cycles.use_denoising and crl.use_denoising:
|
||||
yield (n_("Noisy Image"), "RGBA", 'COLOR')
|
||||
if crl.use_pass_shadow_catcher:
|
||||
yield (n_("Noisy Shadow Catcher"), "RGB", 'COLOR')
|
||||
if crl.denoising_store_passes:
|
||||
yield (n_("Denoising Albedo"), "RGB", 'COLOR')
|
||||
yield (n_("Denoising Specular Albedo"), "RGB", 'COLOR')
|
||||
yield (n_("Denoising Normal"), "XYZ", 'VECTOR')
|
||||
yield (n_("Denoising Roughness"), "X", 'VALUE')
|
||||
yield (n_("Denoising Depth"), "Z", 'VALUE')
|
||||
|
||||
# Custom AOV passes.
|
||||
for aov in srl.aovs:
|
||||
if not aov.is_valid:
|
||||
continue
|
||||
if aov.type == 'VALUE':
|
||||
yield (aov.name, "X", 'VALUE')
|
||||
else:
|
||||
yield (aov.name, "RGBA", 'COLOR')
|
||||
|
||||
# Light groups.
|
||||
for lightgroup in srl.lightgroups:
|
||||
yield ("Combined_%s" % lightgroup.name, "RGB", 'COLOR')
|
||||
|
||||
# Path guiding debug passes.
|
||||
if _cycles.with_debug and scene.cycles.use_guiding:
|
||||
yield (n_("Guiding Color"), "RGB", 'COLOR')
|
||||
yield (n_("Guiding Probability"), "X", 'VALUE')
|
||||
yield (n_("Guiding Average Roughness"), "X", 'VALUE')
|
||||
|
||||
|
||||
def register_passes(engine, scene, view_layer):
|
||||
for name, channelids, channeltype in list_render_passes(scene, view_layer):
|
||||
engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype)
|
||||
106
blender-5.2.0/intern/cycles/blender/addon/maketx.py
Normal file
106
blender-5.2.0/intern/cycles/blender/addon/maketx.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def argparse_create():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=pathlib.Path(sys.argv[0]).name + " --command maketx",
|
||||
description=(
|
||||
"Generate Cycles texture cache (.tx) files.\n"
|
||||
"\n"
|
||||
"When a file path is given, generate a tx file for that image.\n"
|
||||
"When no file path is given, a blend file must be loaded and tx\n"
|
||||
"files are generated for all images used by shader node trees."
|
||||
),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"filepath",
|
||||
type=str,
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Source image file path. If omitted, generate for all images in the blend file.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--colorspace",
|
||||
dest="colorspace",
|
||||
type=str,
|
||||
default="auto",
|
||||
help="Color space name. Auto-detected if omitted. Only used with a file path.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--alpha-type",
|
||||
dest="alpha_type",
|
||||
type=str,
|
||||
default="auto",
|
||||
choices=["straight", "premultiplied", "channel_packed", "none", "auto"],
|
||||
help="Alpha type. Auto-detected if omitted. Only used with a file path.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--cache-dir",
|
||||
dest="cache_dir",
|
||||
type=str,
|
||||
default="",
|
||||
help="Output directory for tx files, relative to source image or absolute.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def maketx_command(argv):
|
||||
parser = argparse_create()
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.filepath is not None:
|
||||
return maketx_file(args)
|
||||
else:
|
||||
return maketx_blend(args, parser)
|
||||
|
||||
|
||||
def maketx_file(args):
|
||||
import _cycles
|
||||
|
||||
filepath = str(pathlib.Path(args.filepath).absolute())
|
||||
|
||||
try:
|
||||
out_filepath = _cycles.maketx(
|
||||
filepath,
|
||||
colorspace=args.colorspace,
|
||||
alpha_type=args.alpha_type,
|
||||
cache_dir=args.cache_dir,
|
||||
)
|
||||
except RuntimeError as ex:
|
||||
sys.stderr.write("Error: {:s}\n".format(str(ex)))
|
||||
return 1
|
||||
|
||||
print(out_filepath)
|
||||
return 0
|
||||
|
||||
|
||||
def maketx_blend(args, parser):
|
||||
import bpy
|
||||
|
||||
if not bpy.data.filepath:
|
||||
parser.error("No image file path given and no blend file loaded.")
|
||||
|
||||
if args.colorspace != "auto":
|
||||
parser.error("--colorspace is only used with an image file path argument.")
|
||||
|
||||
if args.alpha_type != "auto":
|
||||
parser.error("--alpha-type is only used with an image file path argument.")
|
||||
|
||||
if args.cache_dir:
|
||||
bpy.context.preferences.filepaths.texture_cache_directory = args.cache_dir
|
||||
|
||||
bpy.ops.render.generate_texture_cache()
|
||||
return 0
|
||||
167
blender-5.2.0/intern/cycles/blender/addon/operators.py
Normal file
167
blender-5.2.0/intern/cycles/blender/addon/operators.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import StringProperty
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
class CYCLES_OT_use_shading_nodes(Operator):
|
||||
"""Enable nodes on a light"""
|
||||
bl_idname = "cycles.use_shading_nodes"
|
||||
bl_label = "Use Nodes"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return getattr(context, "light", False)
|
||||
|
||||
def execute(self, context):
|
||||
if context.light:
|
||||
context.light.use_nodes = True
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CYCLES_OT_denoise_animation(Operator):
|
||||
"Denoise rendered animation sequence using current scene and view " \
|
||||
"layer settings. Requires denoising data passes and output to " \
|
||||
"OpenEXR multilayer files"
|
||||
bl_idname = "cycles.denoise_animation"
|
||||
bl_label = "Denoise Animation"
|
||||
|
||||
input_filepath: StringProperty(
|
||||
name='Input Filepath',
|
||||
description='File path for image to denoise. If not specified, uses the render file path and frame range from the scene',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
output_filepath: StringProperty(
|
||||
name='Output Filepath',
|
||||
description='If not specified, renders will be denoised in-place',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
def execute(self, context):
|
||||
import os
|
||||
|
||||
preferences = context.preferences
|
||||
scene = context.scene
|
||||
view_layer = context.view_layer
|
||||
|
||||
in_filepath = self.input_filepath
|
||||
out_filepath = self.output_filepath
|
||||
|
||||
in_filepaths = []
|
||||
out_filepaths = []
|
||||
|
||||
if in_filepath != '':
|
||||
# Denoise a single file
|
||||
if out_filepath == '':
|
||||
out_filepath = in_filepath
|
||||
|
||||
in_filepaths.append(in_filepath)
|
||||
out_filepaths.append(out_filepath)
|
||||
else:
|
||||
# Denoise animation sequence with expanded frames matching
|
||||
# Blender render output file naming.
|
||||
in_filepath = scene.render.filepath
|
||||
if out_filepath == '':
|
||||
out_filepath = in_filepath
|
||||
|
||||
# Backup since we will overwrite the scene path temporarily
|
||||
original_filepath = scene.render.filepath
|
||||
|
||||
for frame in range(scene.frame_start, scene.frame_end + 1):
|
||||
scene.render.filepath = in_filepath
|
||||
filepath = scene.render.frame_path(frame=frame)
|
||||
in_filepaths.append(filepath)
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
scene.render.filepath = original_filepath
|
||||
err_msg = tip_("Frame '%s' not found, animation must be complete") % filepath
|
||||
self.report({'ERROR'}, err_msg)
|
||||
return {'CANCELLED'}
|
||||
|
||||
scene.render.filepath = out_filepath
|
||||
filepath = scene.render.frame_path(frame=frame)
|
||||
out_filepaths.append(filepath)
|
||||
|
||||
scene.render.filepath = original_filepath
|
||||
|
||||
# Run denoiser
|
||||
# TODO: support cancel and progress reports.
|
||||
import _cycles
|
||||
try:
|
||||
_cycles.denoise(preferences.as_pointer(),
|
||||
scene.as_pointer(),
|
||||
view_layer.as_pointer(),
|
||||
input=in_filepaths,
|
||||
output=out_filepaths)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, str(e))
|
||||
return {'FINISHED'}
|
||||
|
||||
self.report({'INFO'}, "Denoising completed")
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CYCLES_OT_merge_images(Operator):
|
||||
"Combine OpenEXR multi-layer images rendered with different sample " \
|
||||
"ranges into one image with reduced noise"
|
||||
bl_idname = "cycles.merge_images"
|
||||
bl_label = "Merge Images"
|
||||
|
||||
input_filepath1: StringProperty(
|
||||
name='Input Filepath',
|
||||
description='File path for image to merge',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
input_filepath2: StringProperty(
|
||||
name='Input Filepath',
|
||||
description='File path for image to merge',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
output_filepath: StringProperty(
|
||||
name='Output Filepath',
|
||||
description='File path for merged image',
|
||||
default='',
|
||||
subtype='FILE_PATH')
|
||||
|
||||
def execute(self, context):
|
||||
in_filepaths = [self.input_filepath1, self.input_filepath2]
|
||||
out_filepath = self.output_filepath
|
||||
|
||||
import _cycles
|
||||
try:
|
||||
_cycles.merge(input=in_filepaths, output=out_filepath)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, str(e))
|
||||
return {'FINISHED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
CYCLES_OT_use_shading_nodes,
|
||||
CYCLES_OT_denoise_animation,
|
||||
CYCLES_OT_merge_images
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
375
blender-5.2.0/intern/cycles/blender/addon/osl.py
Normal file
375
blender-5.2.0/intern/cycles/blender/addon/osl.py
Normal file
@@ -0,0 +1,375 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
import _cycles
|
||||
|
||||
from bpy.app.translations import pgettext_rpt as rpt_
|
||||
|
||||
|
||||
def osl_compile(input_path, report):
|
||||
"""compile .osl file with given filepath to temporary .oso file"""
|
||||
import tempfile
|
||||
output_file = tempfile.NamedTemporaryFile(mode='w', suffix=".oso", delete=False)
|
||||
output_path = output_file.name
|
||||
output_file.close()
|
||||
|
||||
ok = _cycles.osl_compile(input_path, output_path)
|
||||
|
||||
if ok:
|
||||
report({'INFO'}, "OSL shader compilation succeeded")
|
||||
|
||||
return ok, output_path
|
||||
|
||||
|
||||
def shader_param_type_default(param, is_bool):
|
||||
if param.isclosure:
|
||||
return 'NodeSocketShader', None
|
||||
elif param.type.vecsemantics == param.type.vecsemantics.COLOR:
|
||||
return 'NodeSocketColor', (param.value[0], param.value[1], param.value[2], 1.0)
|
||||
elif param.type.vecsemantics in [
|
||||
param.type.vecsemantics.POINT,
|
||||
param.type.vecsemantics.VECTOR,
|
||||
param.type.vecsemantics.NORMAL,
|
||||
]:
|
||||
return 'NodeSocketVector', param.value
|
||||
elif param.type.aggregate == param.type.aggregate.SCALAR:
|
||||
if param.type.basetype == param.type.basetype.INT:
|
||||
if is_bool:
|
||||
return 'NodeSocketBool', bool(param.value)
|
||||
else:
|
||||
return 'NodeSocketInt', int(param.value)
|
||||
elif param.type.basetype == param.type.basetype.FLOAT:
|
||||
return 'NodeSocketFloat', float(param.value)
|
||||
elif param.type.basetype == param.type.basetype.STRING:
|
||||
return 'NodeSocketString', str(param.value)
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def shader_param_ensure(node, param):
|
||||
# Skip unsupported types
|
||||
if param.varlenarray or param.isstruct or param.type.arraylen > 1:
|
||||
return None
|
||||
|
||||
metadata = {meta.name: meta.value for meta in param.metadata}
|
||||
|
||||
is_bool = metadata.get('widget') in ['boolean', 'checkBox']
|
||||
hide_value = (param.value is None) or (metadata.get('widget') == 'null')
|
||||
label = metadata.get('label', param.name)
|
||||
|
||||
socket_type, default = shader_param_type_default(param, is_bool)
|
||||
if not socket_type:
|
||||
return None
|
||||
|
||||
sockets = node.outputs if param.isoutput else node.inputs
|
||||
if param.name in sockets:
|
||||
sock = sockets[param.name]
|
||||
|
||||
if sock.bl_idname != socket_type:
|
||||
# Type doesn't match, delete the socket and recreate it below
|
||||
sockets.remove(sock)
|
||||
else:
|
||||
# Update properties if needed
|
||||
if sock.name != label:
|
||||
sock.name = label
|
||||
if not param.isoutput and sock.hide_value != hide_value:
|
||||
sock.hide_value = hide_value
|
||||
|
||||
# We have a matching socket, no need to create one
|
||||
return sock
|
||||
|
||||
sock = sockets.new(type=socket_type, name=label, identifier=param.name)
|
||||
if default is not None:
|
||||
sock.default_value = default
|
||||
sock.hide_value = hide_value
|
||||
return sock
|
||||
|
||||
|
||||
def osl_param_ensure_property(ccam, param):
|
||||
import idprop
|
||||
|
||||
if param.isoutput or param.isclosure:
|
||||
return None
|
||||
|
||||
# Get metadata for the parameter to control UI display
|
||||
metadata = {meta.name: meta.value for meta in param.metadata}
|
||||
if 'label' not in metadata:
|
||||
metadata['label'] = param.name
|
||||
|
||||
datatype = None
|
||||
if param.type.basetype == param.type.basetype.INT:
|
||||
datatype = int
|
||||
elif param.type.basetype == param.type.basetype.FLOAT:
|
||||
datatype = float
|
||||
elif param.type.basetype == param.type.basetype.STRING:
|
||||
datatype = str
|
||||
|
||||
# OSl doesn't have boolean as a type, but we do
|
||||
if (datatype == int) and (metadata.get('widget') in ('boolean', 'checkBox')):
|
||||
datatype = bool
|
||||
default = param.value if isinstance(param.value, tuple) else [param.value]
|
||||
default = [datatype(v) for v in default]
|
||||
|
||||
name = param.name
|
||||
if name in ccam:
|
||||
# If the parameter already exists, only reset its value if its type
|
||||
# or array length changed
|
||||
cur_data = ccam[name]
|
||||
if isinstance(cur_data, idprop.types.IDPropertyArray):
|
||||
cur_length = len(cur_data)
|
||||
cur_type = type(cur_data[0])
|
||||
else:
|
||||
cur_length = 1
|
||||
cur_type = type(cur_data)
|
||||
do_replace = datatype != cur_type or len(default) != cur_length
|
||||
else:
|
||||
# Parameter doesn't exist yet, so set it from the defaults
|
||||
do_replace = True
|
||||
|
||||
if do_replace:
|
||||
ccam[name] = tuple(default) if len(default) > 1 else default[0]
|
||||
|
||||
ui = ccam.id_properties_ui(name)
|
||||
ui.clear()
|
||||
ui.update(default=tuple(default) if len(default) > 1 else default[0])
|
||||
|
||||
# Determine subtype (limited unit support for now)
|
||||
if param.type.vecsemantics == param.type.vecsemantics.COLOR:
|
||||
ui.update(subtype='COLOR')
|
||||
elif param.type.vecsemantics == param.type.vecsemantics.POINT:
|
||||
ui.update(subtype='TRANSLATION')
|
||||
elif param.type.vecsemantics == param.type.vecsemantics.NORMAL:
|
||||
ui.update(subtype='DIRECTION')
|
||||
elif datatype is str and metadata.get('widget') == 'filename':
|
||||
ui.update(subtype='FILE_PATH')
|
||||
elif datatype is float and metadata.get('unit') == 'radians':
|
||||
ui.update(subtype='ANGLE')
|
||||
elif datatype is float and metadata.get('unit') == 'm':
|
||||
ui.update(subtype='DISTANCE')
|
||||
elif datatype is float and metadata.get('unit') == 'mm':
|
||||
ui.update(subtype='DISTANCE_CAMERA')
|
||||
elif datatype is float and metadata.get('unit') in ('s', 'sec'):
|
||||
ui.update(subtype='TIME_ABSOLUTE')
|
||||
elif metadata.get('slider'):
|
||||
ui.update(subtype='FACTOR')
|
||||
elif datatype is int and metadata.get('widget') == 'mapper':
|
||||
options = metadata.get('options', "")
|
||||
options = options.split("|")
|
||||
option_items = []
|
||||
for option in options:
|
||||
if ":" not in option:
|
||||
continue
|
||||
item, index = option.split(":")
|
||||
# Ensure that the index can be converted to an integer
|
||||
try:
|
||||
int(index)
|
||||
except ValueError:
|
||||
continue
|
||||
option_items.append((str(index), bpy.path.display_name(item), ""))
|
||||
ui.update(items=option_items)
|
||||
|
||||
# Map OSL metadata to Blender names
|
||||
option_map = {
|
||||
'help': 'description',
|
||||
'sensitivity': 'step', 'digits': 'precision',
|
||||
'min': 'min', 'max': 'max',
|
||||
'slidermin': 'soft_min', 'slidermax': 'soft_max',
|
||||
}
|
||||
if 'sensitivity' in metadata:
|
||||
# Blender divides this value by 100 by convention, so counteract that.
|
||||
metadata['sensitivity'] *= 100
|
||||
for option, value in metadata.items():
|
||||
if option in option_map:
|
||||
ui.update(**{option_map[option]: value})
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def update_external_script(report, filepath, library):
|
||||
"""compile and update OSL script"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
oso_file_remove = False
|
||||
|
||||
script_path = bpy.path.abspath(filepath, library=library)
|
||||
script_path_noext, script_ext = os.path.splitext(script_path)
|
||||
|
||||
if script_ext == ".oso":
|
||||
# it's a .oso file, no need to compile
|
||||
ok, oso_path = True, script_path
|
||||
elif script_ext == ".osl":
|
||||
# compile .osl file
|
||||
ok, oso_path = osl_compile(script_path, report)
|
||||
oso_file_remove = True
|
||||
|
||||
if ok:
|
||||
# copy .oso from temporary path to .osl directory
|
||||
dst_path = script_path_noext + ".oso"
|
||||
try:
|
||||
shutil.copy2(oso_path, dst_path)
|
||||
except:
|
||||
report({'ERROR'}, rpt_("Failed to write .oso file next to external .osl file at {:s}").format(dst_path))
|
||||
elif os.path.dirname(filepath) == "":
|
||||
# module in search path
|
||||
oso_path = filepath
|
||||
ok = True
|
||||
else:
|
||||
# unknown
|
||||
report({'ERROR'}, "External shader script must have .osl or .oso extension, or be a module name")
|
||||
ok = False
|
||||
|
||||
return ok, oso_path, oso_file_remove
|
||||
|
||||
|
||||
def update_internal_script(report, script):
|
||||
"""compile and update shader script node"""
|
||||
import os
|
||||
import tempfile
|
||||
import pathlib
|
||||
import hashlib
|
||||
|
||||
bytecode = None
|
||||
bytecode_hash = None
|
||||
|
||||
osl_path = bpy.path.abspath(script.filepath, library=script.library)
|
||||
|
||||
if script.is_in_memory or script.is_dirty or script.is_modified or not os.path.exists(osl_path):
|
||||
# write text datablock contents to temporary file
|
||||
osl_file = tempfile.NamedTemporaryFile(mode='w', suffix=".osl", delete=False)
|
||||
osl_file.write(script.as_string())
|
||||
osl_file.write("\n")
|
||||
osl_file.close()
|
||||
|
||||
ok, oso_path = osl_compile(osl_file.name, report)
|
||||
os.remove(osl_file.name)
|
||||
else:
|
||||
# compile text datablock from disk directly
|
||||
ok, oso_path = osl_compile(osl_path, report)
|
||||
|
||||
if ok:
|
||||
# read bytecode
|
||||
try:
|
||||
bytecode = pathlib.Path(oso_path).read_text()
|
||||
md5 = hashlib.md5(usedforsecurity=False)
|
||||
md5.update(bytecode.encode())
|
||||
bytecode_hash = md5.hexdigest()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
report({'ERROR'}, "Cannot read OSO bytecode to store in node at {!r}".format(oso_path))
|
||||
ok = False
|
||||
|
||||
return ok, oso_path, bytecode, bytecode_hash
|
||||
|
||||
|
||||
def update_script_node(node, report):
|
||||
"""compile and update shader script node"""
|
||||
import os
|
||||
import oslquery
|
||||
|
||||
oso_file_remove = False
|
||||
|
||||
if node.mode == 'EXTERNAL':
|
||||
# compile external script file
|
||||
ok, oso_path, oso_file_remove = update_external_script(report, node.filepath, node.id_data.library)
|
||||
if ok:
|
||||
# Clear old internal bytecode, and also trigger node update if it was already cleared.
|
||||
node.bytecode = ""
|
||||
node.bytecode_hash = ""
|
||||
|
||||
elif node.mode == 'INTERNAL' and node.script:
|
||||
# internal script, we will store bytecode in the node
|
||||
ok, oso_path, bytecode, bytecode_hash = update_internal_script(report, node.script)
|
||||
if bytecode:
|
||||
node.bytecode = bytecode
|
||||
node.bytecode_hash = bytecode_hash
|
||||
|
||||
else:
|
||||
report({'WARNING'}, "No text or file specified in node, nothing to compile")
|
||||
return
|
||||
|
||||
if ok:
|
||||
if query := oslquery.OSLQuery(oso_path):
|
||||
# Ensure that all parameters have a matching socket
|
||||
used_sockets = set()
|
||||
for param in query.parameters:
|
||||
if sock := shader_param_ensure(node, param):
|
||||
used_sockets.add(sock)
|
||||
|
||||
# Remove unused sockets
|
||||
for sockets in (node.inputs, node.outputs):
|
||||
for identifier in [sock.identifier for sock in sockets]:
|
||||
if sockets[identifier] not in used_sockets:
|
||||
sockets.remove(sockets[identifier])
|
||||
else:
|
||||
ok = False
|
||||
report({'ERROR'}, rpt_("OSL query failed to open %s") % oso_path)
|
||||
else:
|
||||
report({'ERROR'}, "OSL script compilation failed, see console for errors")
|
||||
|
||||
# remove temporary oso file
|
||||
if oso_file_remove:
|
||||
try:
|
||||
os.remove(oso_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
def update_custom_camera_shader(cam, report):
|
||||
"""compile and update custom camera shader"""
|
||||
import os
|
||||
import oslquery
|
||||
|
||||
oso_file_remove = False
|
||||
|
||||
custom_props = cam.cycles_custom
|
||||
if cam.custom_mode == 'EXTERNAL':
|
||||
# compile external script file
|
||||
ok, oso_path, oso_file_remove = update_external_script(report, cam.custom_filepath, cam.library)
|
||||
|
||||
elif cam.custom_mode == 'INTERNAL' and cam.custom_shader:
|
||||
# internal script, we will store bytecode in the node
|
||||
ok, oso_path, bytecode, bytecode_hash = update_internal_script(report, cam.custom_shader)
|
||||
if bytecode:
|
||||
cam.custom_bytecode = bytecode
|
||||
cam.custom_bytecode_hash = bytecode_hash
|
||||
cam.update_tag()
|
||||
|
||||
else:
|
||||
report({'WARNING'}, "No text or file specified in node, nothing to compile")
|
||||
return
|
||||
|
||||
if ok:
|
||||
if query := oslquery.OSLQuery(oso_path):
|
||||
# Ensure that all parameters have a matching property
|
||||
used_params = set()
|
||||
for param in query.parameters:
|
||||
if name := osl_param_ensure_property(custom_props, param):
|
||||
used_params.add(name)
|
||||
|
||||
# Clean up unused parameters
|
||||
for prop in list(custom_props.keys()):
|
||||
if prop not in used_params:
|
||||
del custom_props[prop]
|
||||
else:
|
||||
ok = False
|
||||
report({'ERROR'}, rpt_("OSL query failed to open %s") % oso_path)
|
||||
else:
|
||||
report({'ERROR'}, "Custom Camera shader compilation failed, see console for errors")
|
||||
|
||||
# remove temporary oso file
|
||||
if oso_file_remove:
|
||||
try:
|
||||
os.remove(oso_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
return ok
|
||||
136
blender-5.2.0/intern/cycles/blender/addon/presets.py
Normal file
136
blender-5.2.0/intern/cycles/blender/addon/presets.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from bl_operators.presets import AddPresetBase
|
||||
from bpy.types import Operator
|
||||
|
||||
|
||||
class AddPresetIntegrator(AddPresetBase, Operator):
|
||||
'''Add an Integrator Preset'''
|
||||
bl_idname = "render.cycles_integrator_preset_add"
|
||||
bl_label = "Add Integrator Preset"
|
||||
preset_menu = "CYCLES_PT_integrator_presets"
|
||||
|
||||
preset_defines = [
|
||||
"cycles = bpy.context.scene.cycles"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"cycles.max_bounces",
|
||||
"cycles.diffuse_bounces",
|
||||
"cycles.glossy_bounces",
|
||||
"cycles.transmission_bounces",
|
||||
"cycles.volume_bounces",
|
||||
"cycles.transparent_max_bounces",
|
||||
"cycles.caustics_reflective",
|
||||
"cycles.caustics_refractive",
|
||||
"cycles.blur_glossy",
|
||||
"cycles.use_fast_gi",
|
||||
"cycles.ao_bounces",
|
||||
"cycles.ao_bounces_render",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/integrator"
|
||||
|
||||
|
||||
class AddPresetSampling(AddPresetBase, Operator):
|
||||
'''Add a Sampling Preset'''
|
||||
bl_idname = "render.cycles_sampling_preset_add"
|
||||
bl_label = "Add Sampling Preset"
|
||||
preset_menu = "CYCLES_PT_sampling_presets"
|
||||
|
||||
preset_defines = [
|
||||
"cycles = bpy.context.scene.cycles"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"cycles.use_adaptive_sampling",
|
||||
"cycles.samples",
|
||||
"cycles.adaptive_threshold",
|
||||
"cycles.adaptive_min_samples",
|
||||
"cycles.time_limit",
|
||||
"cycles.use_denoising",
|
||||
"cycles.denoiser",
|
||||
"cycles.denoising_input_passes",
|
||||
"cycles.denoising_prefilter",
|
||||
"cycles.denoising_quality",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/sampling"
|
||||
|
||||
|
||||
class AddPresetViewportSampling(AddPresetBase, Operator):
|
||||
'''Add a Viewport Sampling Preset'''
|
||||
bl_idname = "render.cycles_viewport_sampling_preset_add"
|
||||
bl_label = "Add Viewport Sampling Preset"
|
||||
preset_menu = "CYCLES_PT_viewport_sampling_presets"
|
||||
|
||||
preset_defines = [
|
||||
"cycles = bpy.context.scene.cycles"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"cycles.use_preview_adaptive_sampling",
|
||||
"cycles.preview_samples",
|
||||
"cycles.preview_adaptive_threshold",
|
||||
"cycles.preview_adaptive_min_samples",
|
||||
"cycles.use_preview_denoising",
|
||||
"cycles.preview_denoiser",
|
||||
"cycles.preview_denoising_input_passes",
|
||||
"cycles.preview_denoising_prefilter",
|
||||
"cycles.preview_denoising_quality",
|
||||
"cycles.preview_denoising_start_sample",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/viewport_sampling"
|
||||
|
||||
|
||||
class AddPresetPerformance(AddPresetBase, Operator):
|
||||
'''Add an Performance Preset'''
|
||||
bl_idname = "render.cycles_performance_preset_add"
|
||||
bl_label = "Add Performance Preset"
|
||||
preset_menu = "CYCLES_PT_performance_presets"
|
||||
|
||||
preset_defines = [
|
||||
"render = bpy.context.scene.render",
|
||||
"cycles = bpy.context.scene.cycles",
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"render.threads_mode",
|
||||
"render.use_persistent_data",
|
||||
"cycles.debug_use_spatial_splits",
|
||||
"cycles.debug_use_compact_bvh",
|
||||
"cycles.debug_use_hair_bvh",
|
||||
"cycles.debug_bvh_time_steps",
|
||||
"cycles.tile_size",
|
||||
]
|
||||
|
||||
preset_subdir = "cycles/performance"
|
||||
|
||||
|
||||
classes = (
|
||||
AddPresetIntegrator,
|
||||
AddPresetSampling,
|
||||
AddPresetViewportSampling,
|
||||
AddPresetPerformance,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
2063
blender-5.2.0/intern/cycles/blender/addon/properties.py
Normal file
2063
blender-5.2.0/intern/cycles/blender/addon/properties.py
Normal file
File diff suppressed because it is too large
Load Diff
2708
blender-5.2.0/intern/cycles/blender/addon/ui.py
Normal file
2708
blender-5.2.0/intern/cycles/blender/addon/ui.py
Normal file
File diff suppressed because it is too large
Load Diff
330
blender-5.2.0/intern/cycles/blender/addon/version_update.py
Normal file
330
blender-5.2.0/intern/cycles/blender/addon/version_update.py
Normal file
@@ -0,0 +1,330 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
def custom_bake_remap(scene):
|
||||
"""
|
||||
Remap bake types into the new types and set the flags accordingly
|
||||
"""
|
||||
bake_lookup = (
|
||||
'COMBINED',
|
||||
'AO',
|
||||
'SHADOW',
|
||||
'NORMAL',
|
||||
'UV',
|
||||
'EMIT',
|
||||
'ENVIRONMENT',
|
||||
'DIFFUSE_DIRECT',
|
||||
'DIFFUSE_INDIRECT',
|
||||
'DIFFUSE_COLOR',
|
||||
'GLOSSY_DIRECT',
|
||||
'GLOSSY_INDIRECT',
|
||||
'GLOSSY_COLOR',
|
||||
'TRANSMISSION_DIRECT',
|
||||
'TRANSMISSION_INDIRECT',
|
||||
'TRANSMISSION_COLOR')
|
||||
|
||||
diffuse_direct_idx = bake_lookup.index('DIFFUSE_DIRECT')
|
||||
|
||||
cscene = scene.cycles
|
||||
|
||||
# Old bake type
|
||||
bake_type_idx = cscene.get("bake_type")
|
||||
|
||||
if bake_type_idx is None:
|
||||
cscene.bake_type = 'COMBINED'
|
||||
return
|
||||
|
||||
# File doesn't need versioning
|
||||
if bake_type_idx < diffuse_direct_idx:
|
||||
return
|
||||
|
||||
# File needs versioning
|
||||
bake_type = bake_lookup[bake_type_idx]
|
||||
cscene.bake_type, end = bake_type.split('_')
|
||||
|
||||
if end == 'DIRECT':
|
||||
scene.render.bake.use_pass_indirect = False
|
||||
scene.render.bake.use_pass_color = False
|
||||
|
||||
elif end == 'INDIRECT':
|
||||
scene.render.bake.use_pass_direct = False
|
||||
scene.render.bake.use_pass_color = False
|
||||
|
||||
elif end == 'COLOR':
|
||||
scene.render.bake.use_pass_direct = False
|
||||
scene.render.bake.use_pass_indirect = False
|
||||
|
||||
|
||||
@persistent
|
||||
def do_versions(self):
|
||||
if bpy.context.preferences.version <= (2, 78, 1):
|
||||
prop = bpy.context.preferences.addons[__package__].preferences
|
||||
system = bpy.context.preferences.system
|
||||
if not prop.is_property_set("compute_device_type"):
|
||||
# Device might not currently be available so this can fail
|
||||
try:
|
||||
if system.legacy_compute_device_type == 1:
|
||||
prop.compute_device_type = 'NONE' # Was OpenCL
|
||||
elif system.legacy_compute_device_type == 2:
|
||||
prop.compute_device_type = 'CUDA'
|
||||
else:
|
||||
prop.compute_device_type = 'NONE'
|
||||
except:
|
||||
pass
|
||||
|
||||
# Init device list for UI
|
||||
prop.get_devices(prop.compute_device_type)
|
||||
|
||||
if bpy.context.preferences.version <= (3, 0, 40):
|
||||
# Disable OpenCL device
|
||||
prop = bpy.context.preferences.addons[__package__].preferences
|
||||
if prop.is_property_set("compute_device_type") and prop['compute_device_type'] == 4:
|
||||
prop.compute_device_type = 'NONE'
|
||||
|
||||
# We don't modify startup file because it assumes to
|
||||
# have all the default values only.
|
||||
if not bpy.data.is_saved:
|
||||
return
|
||||
|
||||
# Map of versions used by libraries.
|
||||
library_versions = {}
|
||||
library_versions[bpy.data.version] = [None]
|
||||
for library in bpy.data.libraries:
|
||||
library_versions.setdefault(library.version, []).append(library)
|
||||
|
||||
# Do versioning per library, since they might have different versions.
|
||||
max_need_versioning = (5, 2, 8)
|
||||
for version, libraries in library_versions.items():
|
||||
if version > max_need_versioning:
|
||||
continue
|
||||
|
||||
# Scenes
|
||||
for scene in bpy.data.scenes:
|
||||
if scene.library not in libraries:
|
||||
continue
|
||||
|
||||
# Auto tiling is always enabled now
|
||||
if version <= (5, 0, 77):
|
||||
cscene = scene.cycles
|
||||
if not cscene.use_auto_tile:
|
||||
cscene.use_auto_tile = True
|
||||
cscene.tile_size = 8192
|
||||
|
||||
# Clamp Direct/Indirect separation in 270
|
||||
if version <= (2, 70, 0):
|
||||
cscene = scene.cycles
|
||||
sample_clamp = cscene.get("sample_clamp", False)
|
||||
if (sample_clamp and
|
||||
not cscene.is_property_set("sample_clamp_direct") and
|
||||
not cscene.is_property_set("sample_clamp_indirect")):
|
||||
cscene.sample_clamp_direct = sample_clamp
|
||||
cscene.sample_clamp_indirect = sample_clamp
|
||||
|
||||
# Change of Volume Bounces in 271
|
||||
if version <= (2, 71, 0):
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("volume_bounces"):
|
||||
cscene.volume_bounces = 1
|
||||
|
||||
# Caustics Reflective/Refractive separation in 272
|
||||
if version <= (2, 72, 0):
|
||||
cscene = scene.cycles
|
||||
if (
|
||||
cscene.get("no_caustics", False) and
|
||||
not cscene.is_property_set("caustics_reflective") and
|
||||
not cscene.is_property_set("caustics_refractive")
|
||||
):
|
||||
cscene.caustics_reflective = False
|
||||
cscene.caustics_refractive = False
|
||||
|
||||
# Baking types changed
|
||||
if version <= (2, 76, 6):
|
||||
custom_bake_remap(scene)
|
||||
|
||||
# Several default changes for 2.77
|
||||
if version <= (2, 76, 8):
|
||||
cscene = scene.cycles
|
||||
|
||||
# Samples
|
||||
if not cscene.is_property_set("samples"):
|
||||
cscene.samples = 10
|
||||
|
||||
# Preview Samples
|
||||
if not cscene.is_property_set("preview_samples"):
|
||||
cscene.preview_samples = 10
|
||||
|
||||
# Filter
|
||||
if cscene.get("filter_type", -1) == -1:
|
||||
cscene.pixel_filter_type = 'GAUSSIAN'
|
||||
|
||||
if version <= (2, 76, 10):
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("pixel_filter_type"):
|
||||
filter_type_int = cscene.get("filter_type", -1)
|
||||
if filter_type_int == 0:
|
||||
cscene.pixel_filter_type = 'BOX'
|
||||
elif filter_type_int == 1:
|
||||
cscene.pixel_filter_type = 'GAUSSIAN'
|
||||
|
||||
if version <= (2, 78, 2):
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("light_sampling_threshold"):
|
||||
cscene.light_sampling_threshold = 0.0
|
||||
|
||||
if version <= (2, 79, 0):
|
||||
cscene = scene.cycles
|
||||
# Default changes
|
||||
if not cscene.is_property_set("blur_glossy"):
|
||||
cscene.blur_glossy = 0.0
|
||||
if not cscene.is_property_set("sample_clamp_indirect"):
|
||||
cscene.sample_clamp_indirect = 0.0
|
||||
|
||||
if version <= (2, 92, 4):
|
||||
if scene.render.engine == 'CYCLES':
|
||||
for view_layer in scene.view_layers:
|
||||
cview_layer = view_layer.cycles
|
||||
view_layer.use_pass_cryptomatte_object = cview_layer.get("use_pass_crypto_object", False)
|
||||
view_layer.use_pass_cryptomatte_material = cview_layer.get("use_pass_crypto_material", False)
|
||||
view_layer.use_pass_cryptomatte_asset = cview_layer.get("use_pass_crypto_asset", False)
|
||||
view_layer.pass_cryptomatte_depth = cview_layer.get("pass_crypto_depth", 6)
|
||||
|
||||
if version <= (2, 93, 7):
|
||||
if scene.render.engine == 'CYCLES':
|
||||
for view_layer in scene.view_layers:
|
||||
cview_layer = view_layer.cycles
|
||||
for caov in cview_layer.get("aovs", []):
|
||||
aov_name = caov.get("name", "AOV")
|
||||
if aov_name in view_layer.aovs:
|
||||
continue
|
||||
baov = view_layer.aovs.add()
|
||||
baov.name = caov.get("name", "AOV")
|
||||
baov.type = "COLOR" if caov.get("type", 1) == 1 else "VALUE"
|
||||
|
||||
if version <= (2, 93, 16):
|
||||
cscene = scene.cycles
|
||||
ao_bounces = cscene.get("ao_bounces", 0)
|
||||
ao_bounces_render = cscene.get("ao_bounces_render", 0)
|
||||
if scene.render.use_simplify and (ao_bounces or ao_bounces_render):
|
||||
cscene.use_fast_gi = True
|
||||
cscene.ao_bounces = ao_bounces
|
||||
cscene.ao_bounces_render = ao_bounces_render
|
||||
else:
|
||||
cscene.ao_bounces = 1
|
||||
cscene.ao_bounces_render = 1
|
||||
|
||||
if version <= (3, 0, 25):
|
||||
cscene = scene.cycles
|
||||
|
||||
# Default changes.
|
||||
if not cscene.is_property_set("samples"):
|
||||
cscene.samples = 128
|
||||
if not cscene.is_property_set("preview_samples"):
|
||||
cscene.preview_samples = 32
|
||||
if not cscene.is_property_set("use_adaptive_sampling"):
|
||||
cscene.use_adaptive_sampling = False
|
||||
cscene.use_preview_adaptive_sampling = False
|
||||
if not cscene.is_property_set("use_denoising"):
|
||||
cscene.use_denoising = False
|
||||
if not cscene.is_property_set("use_preview_denoising"):
|
||||
cscene.use_preview_denoising = False
|
||||
if not cscene.is_property_set("sampling_pattern") or \
|
||||
cscene.get('sampling_pattern') >= 2:
|
||||
cscene.sampling_pattern = 'TABULATED_SOBOL'
|
||||
|
||||
# Removal of square samples.
|
||||
cscene = scene.cycles
|
||||
use_square_samples = cscene.get("use_square_samples", False)
|
||||
|
||||
if use_square_samples:
|
||||
cscene.samples *= cscene.samples
|
||||
cscene.preview_samples *= cscene.preview_samples
|
||||
for layer in scene.view_layers:
|
||||
layer.samples *= layer.samples
|
||||
cscene["use_square_samples"] = False
|
||||
|
||||
if version <= (3, 5, 3):
|
||||
cscene = scene.cycles
|
||||
# Disable light tree for existing scenes.
|
||||
if not cscene.is_property_set("use_light_tree"):
|
||||
cscene.use_light_tree = False
|
||||
# Sampling pattern settings are hidden behind a debug menu. Switch to the
|
||||
# default faster and fully featured (Supports Scrambling Distance)
|
||||
# Tabulated Sobol.
|
||||
cscene.sampling_pattern = 'TABULATED_SOBOL'
|
||||
|
||||
if version <= (4, 2, 52):
|
||||
cscene = scene.cycles
|
||||
# Previous versions defaulted to Tabulated Sobol unless debugging options
|
||||
# were enabled, so keep this behavior instead of suddenly defaulting to
|
||||
# blue noise if the file happens to contain a different option for the enum.
|
||||
cscene.sampling_pattern = 'TABULATED_SOBOL'
|
||||
|
||||
# Lamps
|
||||
for light in bpy.data.lights:
|
||||
if light.library not in libraries:
|
||||
continue
|
||||
|
||||
if version <= (2, 76, 5):
|
||||
clight = light.cycles
|
||||
|
||||
# MIS
|
||||
if not clight.is_property_set("use_multiple_importance_sampling"):
|
||||
clight.use_multiple_importance_sampling = False
|
||||
|
||||
# Worlds
|
||||
for world in bpy.data.worlds:
|
||||
if world.library not in libraries:
|
||||
continue
|
||||
|
||||
if version <= (2, 76, 9):
|
||||
cworld = world.cycles
|
||||
|
||||
# World MIS Resolution
|
||||
if not cworld.is_property_set("sample_map_resolution"):
|
||||
cworld.sample_map_resolution = 256
|
||||
|
||||
if version <= (2, 79, 4) or \
|
||||
(version >= (2, 80, 0) and version <= (2, 80, 18)):
|
||||
cworld = world.cycles
|
||||
# World MIS
|
||||
if not cworld.is_property_set("sampling_method"):
|
||||
if cworld.get("sample_as_light", True):
|
||||
cworld.sampling_method = 'MANUAL'
|
||||
else:
|
||||
cworld.sampling_method = 'NONE'
|
||||
|
||||
# Materials
|
||||
for mat in bpy.data.materials:
|
||||
if mat.library not in libraries:
|
||||
continue
|
||||
|
||||
if version <= (2, 76, 5):
|
||||
cmat = mat.cycles
|
||||
# Volume Sampling
|
||||
if not cmat.is_property_set("volume_sampling"):
|
||||
cmat.volume_sampling = 'DISTANCE'
|
||||
|
||||
if version <= (2, 79, 2):
|
||||
cmat = mat.cycles
|
||||
if cmat.get("displacement_method", -1) == -1:
|
||||
cmat['displacement_method'] = 0
|
||||
|
||||
# Change default to bump again.
|
||||
if version <= (2, 79, 6) or \
|
||||
(version >= (2, 80, 0) and version <= (2, 80, 41)):
|
||||
cmat = mat.cycles
|
||||
if cmat.get("displacement_method", -1) == -1:
|
||||
cmat['displacement_method'] = 1
|
||||
|
||||
if version <= (3, 5, 3):
|
||||
cmat = mat.cycles
|
||||
if not cmat.get("sample_as_light", True):
|
||||
cmat.emission_sampling = 'NONE'
|
||||
188
blender-5.2.0/intern/cycles/blender/attribute_convert.h
Normal file
188
blender-5.2.0/intern/cycles/blender/attribute_convert.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/attribute.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/param.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_math_quaternion_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
template<typename BlenderT> struct AttributeConverter {
|
||||
using CyclesT = void;
|
||||
};
|
||||
|
||||
template<> struct AttributeConverter<float> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static constexpr bool layout_compatible = true;
|
||||
static CyclesT convert(const float &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const int &value)
|
||||
{
|
||||
return float(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::float2> {
|
||||
using CyclesT = float2;
|
||||
static constexpr auto type_desc = TypeFloat2;
|
||||
static constexpr bool layout_compatible = true;
|
||||
static CyclesT convert(const blender::float2 &value)
|
||||
{
|
||||
return make_float2(value[0], value[1]);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::float3> {
|
||||
using CyclesT = packed_float3;
|
||||
static constexpr auto type_desc = TypeVector;
|
||||
static constexpr bool layout_compatible = true;
|
||||
static CyclesT convert(const blender::float3 &value)
|
||||
{
|
||||
return packed_float3(make_float3(value[0], value[1], value[2]));
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::float4> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeFloat4;
|
||||
/* Allocation alignment is not compatible with Cycles */
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const blender::float4 &value)
|
||||
{
|
||||
return make_float4(value[0], value[1], value[2], value[3]);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::ColorGeometry4f> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeRGBA;
|
||||
/* Allocation alignment is not compatible with Cycles */
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const blender::ColorGeometry4f &value)
|
||||
{
|
||||
return make_float4(value[0], value[1], value[2], value[3]);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::ColorGeometry4b> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeRGBA;
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const blender::ColorGeometry4b &value)
|
||||
{
|
||||
return color_srgb_to_linear_v4(make_float4(byte_to_float(value[0]),
|
||||
byte_to_float(value[1]),
|
||||
byte_to_float(value[2]),
|
||||
byte_to_float(value[3])));
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<bool> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const bool &value)
|
||||
{
|
||||
return float(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int8_t> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const int8_t &value)
|
||||
{
|
||||
return float(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::math::Quaternion> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeFloat4;
|
||||
/* Allocation alignment is not compatible with Cycles */
|
||||
static constexpr bool layout_compatible = false;
|
||||
static CyclesT convert(const blender::math::Quaternion &value)
|
||||
{
|
||||
return make_float4(value.w, value.x, value.y, value.z);
|
||||
}
|
||||
};
|
||||
|
||||
/* Add a standard attribute from a Blender attribute reader, sharing the buffer
|
||||
* with Blender when possible. */
|
||||
template<typename BlenderT>
|
||||
bool sync_attribute_from_blender(AttributeSet &attributes,
|
||||
const AttributeStandard std,
|
||||
const blender::bke::AttributeReader<BlenderT> &b_reader,
|
||||
const int size)
|
||||
{
|
||||
if (!b_reader) {
|
||||
return false;
|
||||
}
|
||||
using Converter = AttributeConverter<BlenderT>;
|
||||
using CyclesT = typename Converter::CyclesT;
|
||||
|
||||
/* Try implicit sharing. */
|
||||
if constexpr (Converter::layout_compatible) {
|
||||
const blender::CommonVArrayInfo info = b_reader.varray.common_info();
|
||||
if (info.type == blender::CommonVArrayInfo::Type::Span && b_reader.sharing_info) {
|
||||
attributes.add_shared(std, ustring(), info.data, size, b_reader.sharing_info);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise allocate and copy. */
|
||||
Attribute *attr = attributes.add(std);
|
||||
CyclesT *data = attr->data_for_write<CyclesT>();
|
||||
const blender::VArraySpan<BlenderT> src = *b_reader;
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Same as sync_attribute_from_blender, but for a single motion step of an
|
||||
* existing attribute. */
|
||||
template<typename BlenderT>
|
||||
bool sync_attribute_motion_step_from_blender(
|
||||
Attribute &attr,
|
||||
const int motion_step,
|
||||
const blender::bke::AttributeReader<BlenderT> &b_reader)
|
||||
{
|
||||
if (!b_reader) {
|
||||
return false;
|
||||
}
|
||||
using Converter = AttributeConverter<BlenderT>;
|
||||
using CyclesT = typename Converter::CyclesT;
|
||||
|
||||
/* Try implicit sharing. */
|
||||
if constexpr (Converter::layout_compatible) {
|
||||
const blender::CommonVArrayInfo info = b_reader.varray.common_info();
|
||||
if (info.type == blender::CommonVArrayInfo::Type::Span && b_reader.sharing_info) {
|
||||
attr.set_motion_step_shared(
|
||||
motion_step, info.data, b_reader.varray.size(), b_reader.sharing_info);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise allocate and copy. */
|
||||
CyclesT *data = attr.data_for_write<CyclesT>(motion_step);
|
||||
const blender::VArraySpan<BlenderT> src = *b_reader;
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
1238
blender-5.2.0/intern/cycles/blender/camera.cpp
Normal file
1238
blender-5.2.0/intern/cycles/blender/camera.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1237
blender-5.2.0/intern/cycles/blender/curves.cpp
Normal file
1237
blender-5.2.0/intern/cycles/blender/curves.cpp
Normal file
File diff suppressed because it is too large
Load Diff
191
blender-5.2.0/intern/cycles/blender/device.cpp
Normal file
191
blender-5.2.0/intern/cycles/blender/device.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "blender/device.h"
|
||||
#include "blender/session.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "BKE_scene.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
enum ComputeDevice {
|
||||
COMPUTE_DEVICE_CPU = 0,
|
||||
COMPUTE_DEVICE_CUDA = 1,
|
||||
COMPUTE_DEVICE_OPTIX = 3,
|
||||
COMPUTE_DEVICE_HIP = 4,
|
||||
COMPUTE_DEVICE_METAL = 5,
|
||||
COMPUTE_DEVICE_ONEAPI = 6,
|
||||
|
||||
COMPUTE_DEVICE_NUM
|
||||
};
|
||||
|
||||
int blender_device_threads(blender::Scene &b_scene)
|
||||
{
|
||||
blender::RenderData &b_r = b_scene.r;
|
||||
int threads_override = blender::BLI_system_num_threads_override_get();
|
||||
if (threads_override > 0 || (b_r.mode & blender::R_FIXED_THREADS) != 0) {
|
||||
return BKE_render_num_threads(&b_r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adjust_device_info_from_preferences(DeviceInfo &info, blender::PointerRNA cpreferences)
|
||||
{
|
||||
if (!get_boolean(cpreferences, "peer_memory")) {
|
||||
info.has_peer_memory = false;
|
||||
}
|
||||
|
||||
if (info.type == DEVICE_METAL) {
|
||||
const MetalRTSetting use_metalrt = (MetalRTSetting)get_enum(
|
||||
cpreferences, "metalrt", METALRT_NUM_SETTINGS, METALRT_AUTO);
|
||||
|
||||
info.use_hardware_raytracing = info.use_metalrt_by_default;
|
||||
if (use_metalrt == METALRT_OFF) {
|
||||
info.use_hardware_raytracing = false;
|
||||
}
|
||||
else if (use_metalrt == METALRT_ON) {
|
||||
info.use_hardware_raytracing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.type == DEVICE_ONEAPI && !get_boolean(cpreferences, "use_oneapirt")) {
|
||||
info.use_hardware_raytracing = false;
|
||||
}
|
||||
|
||||
if (info.type == DEVICE_HIP && !get_boolean(cpreferences, "use_hiprt")) {
|
||||
info.use_hardware_raytracing = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void adjust_device_info(DeviceInfo &device, blender::PointerRNA cpreferences, bool preview)
|
||||
{
|
||||
adjust_device_info_from_preferences(device, cpreferences);
|
||||
for (DeviceInfo &info : device.multi_devices) {
|
||||
adjust_device_info_from_preferences(info, cpreferences);
|
||||
}
|
||||
|
||||
if (preview) {
|
||||
/* Disable specialization for preview renders. */
|
||||
device.kernel_optimization_level = KERNEL_OPTIMIZATION_LEVEL_OFF;
|
||||
}
|
||||
else {
|
||||
device.kernel_optimization_level = (KernelOptimizationLevel)get_enum(
|
||||
cpreferences,
|
||||
"kernel_optimization_level",
|
||||
KERNEL_OPTIMIZATION_NUM_LEVELS,
|
||||
KERNEL_OPTIMIZATION_LEVEL_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceInfo blender_device_info(blender::UserDef &b_preferences,
|
||||
blender::Scene &b_scene,
|
||||
bool background,
|
||||
bool preview,
|
||||
DeviceInfo &preferences_device)
|
||||
{
|
||||
blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene.id);
|
||||
blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles");
|
||||
|
||||
/* Find cycles preferences. */
|
||||
blender::PointerRNA cpreferences;
|
||||
for (blender::bAddon &b_addon : b_preferences.addons) {
|
||||
if (STREQ(b_addon.module, "cycles")) {
|
||||
blender::PointerRNA addon_rna_ptr = RNA_pointer_create_discrete(
|
||||
nullptr, blender::RNA_Addon, &b_addon);
|
||||
cpreferences = RNA_pointer_get(&addon_rna_ptr, "preferences");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default to CPU device. */
|
||||
DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front();
|
||||
|
||||
/* Device, which is chosen in the Blender Preferences. Default to CPU device. */
|
||||
preferences_device = cpu_device;
|
||||
|
||||
/* Test if we are using GPU devices. */
|
||||
const ComputeDevice compute_device = (ComputeDevice)get_enum(
|
||||
cpreferences, "compute_device_type", COMPUTE_DEVICE_NUM, COMPUTE_DEVICE_CPU);
|
||||
|
||||
if (compute_device != COMPUTE_DEVICE_CPU) {
|
||||
/* Query GPU devices with matching types. */
|
||||
uint mask = DEVICE_MASK_CPU;
|
||||
if (compute_device == COMPUTE_DEVICE_CUDA) {
|
||||
mask |= DEVICE_MASK_CUDA;
|
||||
}
|
||||
else if (compute_device == COMPUTE_DEVICE_OPTIX) {
|
||||
mask |= DEVICE_MASK_OPTIX;
|
||||
}
|
||||
else if (compute_device == COMPUTE_DEVICE_HIP) {
|
||||
mask |= DEVICE_MASK_HIP;
|
||||
}
|
||||
else if (compute_device == COMPUTE_DEVICE_METAL) {
|
||||
mask |= DEVICE_MASK_METAL;
|
||||
}
|
||||
else if (compute_device == COMPUTE_DEVICE_ONEAPI) {
|
||||
mask |= DEVICE_MASK_ONEAPI;
|
||||
}
|
||||
const vector<DeviceInfo> devices = Device::available_devices(mask);
|
||||
|
||||
/* Match device preferences and available devices. */
|
||||
vector<DeviceInfo> used_devices;
|
||||
blender::CollectionPropertyIterator rna_iter;
|
||||
for (RNA_collection_begin(&cpreferences, "devices", &rna_iter); rna_iter.valid;
|
||||
RNA_property_collection_next(&rna_iter))
|
||||
{
|
||||
blender::PointerRNA device = rna_iter.ptr;
|
||||
if (get_boolean(device, "use")) {
|
||||
const string id = get_string(device, "id");
|
||||
for (const DeviceInfo &info : devices) {
|
||||
if (info.id == id) {
|
||||
used_devices.push_back(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
blender::RNA_property_collection_end(&rna_iter);
|
||||
|
||||
if (!used_devices.empty()) {
|
||||
const int threads = blender_device_threads(b_scene);
|
||||
preferences_device = Device::get_multi_device(used_devices, threads, background);
|
||||
}
|
||||
}
|
||||
|
||||
adjust_device_info(preferences_device, cpreferences, preview);
|
||||
adjust_device_info(cpu_device, cpreferences, preview);
|
||||
|
||||
/* Device, which will be used, according to Settings, Scene preferences and command line
|
||||
* parameters. */
|
||||
DeviceInfo device;
|
||||
|
||||
if (BlenderSession::device_override != DEVICE_MASK_ALL) {
|
||||
const vector<DeviceInfo> devices = Device::available_devices(BlenderSession::device_override);
|
||||
|
||||
if (devices.empty()) {
|
||||
device = Device::dummy_device("Found no Cycles device of the specified type");
|
||||
}
|
||||
else {
|
||||
const int threads = blender_device_threads(b_scene);
|
||||
device = Device::get_multi_device(devices, threads, background);
|
||||
}
|
||||
adjust_device_info(device, cpreferences, preview);
|
||||
}
|
||||
else {
|
||||
/* 1 is a "GPU compute" in properties.py for Scene settings. */
|
||||
if (get_enum(cscene, "device") == 1) {
|
||||
device = preferences_device;
|
||||
}
|
||||
else {
|
||||
device = cpu_device;
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/blender/device.h
Normal file
28
blender-5.2.0/intern/cycles/blender/device.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
struct Scene;
|
||||
struct UserDef;
|
||||
} // namespace blender
|
||||
|
||||
#include "device/device.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Get number of threads to use for rendering. */
|
||||
int blender_device_threads(blender::Scene &b_scene);
|
||||
|
||||
/* Convert Blender settings to device specification. In addition, preferences_device contains the
|
||||
* device chosen in Cycles global preferences, which is useful for the denoiser device selection.
|
||||
*/
|
||||
DeviceInfo blender_device_info(blender::UserDef &b_preferences,
|
||||
blender::Scene &b_scene,
|
||||
bool background,
|
||||
bool preview,
|
||||
DeviceInfo &preferences_device);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
907
blender-5.2.0/intern/cycles/blender/display_driver.cpp
Normal file
907
blender-5.2.0/intern/cycles/blender/display_driver.cpp
Normal file
@@ -0,0 +1,907 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_platform.hh"
|
||||
#include "GPU_platform_backend_enum.h"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "blender/display_driver.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/math.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* BlenderDisplayShader.
|
||||
*/
|
||||
|
||||
unique_ptr<BlenderDisplayShader> BlenderDisplayShader::create(blender::RenderEngine &b_engine,
|
||||
blender::Scene &b_scene)
|
||||
{
|
||||
/* See #engine_support_display_space_shader in rna_render.cc. */
|
||||
return make_unique<BlenderDisplaySpaceShader>(b_engine, b_scene);
|
||||
}
|
||||
|
||||
int BlenderDisplayShader::get_position_attrib_location()
|
||||
{
|
||||
if (position_attribute_location_ == -1) {
|
||||
blender::gpu::Shader *shader_program = get_shader_program();
|
||||
position_attribute_location_ = blender::GPU_shader_get_attribute(shader_program,
|
||||
position_attribute_name);
|
||||
}
|
||||
return position_attribute_location_;
|
||||
}
|
||||
|
||||
int BlenderDisplayShader::get_tex_coord_attrib_location()
|
||||
{
|
||||
if (tex_coord_attribute_location_ == -1) {
|
||||
blender::gpu::Shader *shader_program = get_shader_program();
|
||||
tex_coord_attribute_location_ = blender::GPU_shader_get_attribute(shader_program,
|
||||
tex_coord_attribute_name);
|
||||
}
|
||||
return tex_coord_attribute_location_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* BlenderDisplaySpaceShader.
|
||||
*/
|
||||
|
||||
BlenderDisplaySpaceShader::BlenderDisplaySpaceShader(blender::RenderEngine &b_engine,
|
||||
blender::Scene &b_scene)
|
||||
: b_engine_(b_engine), b_scene_(b_scene)
|
||||
{
|
||||
}
|
||||
|
||||
blender::gpu::Shader *BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/)
|
||||
{
|
||||
blender::gpu::Shader *shader = blender::GPU_shader_get_builtin_shader(
|
||||
blender::GPU_SHADER_3D_IMAGE);
|
||||
blender::GPU_shader_bind(shader);
|
||||
/** \note "image" binding slot is 0. */
|
||||
return blender::GPU_shader_get_bound();
|
||||
}
|
||||
|
||||
void BlenderDisplaySpaceShader::unbind()
|
||||
{
|
||||
blender::GPU_shader_unbind();
|
||||
}
|
||||
|
||||
blender::gpu::Shader *BlenderDisplaySpaceShader::get_shader_program()
|
||||
{
|
||||
if (!shader_program_) {
|
||||
shader_program_ = blender::GPU_shader_get_bound();
|
||||
}
|
||||
if (!shader_program_) {
|
||||
LOG_ERROR << "Error retrieving shader program for display space shader.";
|
||||
}
|
||||
|
||||
return shader_program_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* DrawTile.
|
||||
*/
|
||||
|
||||
/* Higher level representation of a texture from the graphics library. */
|
||||
class DisplayGPUTexture {
|
||||
public:
|
||||
/* Global counter for all allocated blender::GPUTextures used by instances of this class. */
|
||||
static inline std::atomic<int> num_used = 0;
|
||||
|
||||
DisplayGPUTexture() = default;
|
||||
|
||||
~DisplayGPUTexture()
|
||||
{
|
||||
assert(gpu_texture == nullptr);
|
||||
}
|
||||
|
||||
DisplayGPUTexture(const DisplayGPUTexture &other) = delete;
|
||||
DisplayGPUTexture &operator=(DisplayGPUTexture &other) = delete;
|
||||
|
||||
DisplayGPUTexture(DisplayGPUTexture &&other) noexcept
|
||||
: gpu_texture(other.gpu_texture), width(other.width), height(other.height)
|
||||
{
|
||||
other.reset();
|
||||
}
|
||||
|
||||
DisplayGPUTexture &operator=(DisplayGPUTexture &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
gpu_texture = other.gpu_texture;
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
|
||||
other.reset();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool gpu_resources_ensure(const uint texture_width, const uint texture_height)
|
||||
{
|
||||
if (width != texture_width || height != texture_height) {
|
||||
gpu_resources_destroy();
|
||||
}
|
||||
|
||||
if (gpu_texture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
width = texture_width;
|
||||
height = texture_height;
|
||||
|
||||
/* Texture must have a minimum size of 1x1. */
|
||||
gpu_texture = blender::GPU_texture_create_2d("CyclesBlitTexture",
|
||||
max(width, 1),
|
||||
max(height, 1),
|
||||
1,
|
||||
blender::gpu::TextureFormat::SFLOAT_16_16_16_16,
|
||||
blender::GPU_TEXTURE_USAGE_GENERAL,
|
||||
nullptr);
|
||||
|
||||
if (!gpu_texture) {
|
||||
LOG_ERROR << "Error creating texture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
blender::GPU_texture_filter_mode(gpu_texture, false);
|
||||
blender::GPU_texture_extend_mode(gpu_texture, blender::GPU_SAMPLER_EXTEND_MODE_EXTEND);
|
||||
|
||||
++num_used;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
if (gpu_texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_TEXTURE_FREE_SAFE(gpu_texture);
|
||||
|
||||
reset();
|
||||
|
||||
--num_used;
|
||||
}
|
||||
|
||||
/* Texture resource allocated by the blender::GPU module.
|
||||
*
|
||||
* NOTE: Allocated on the render engine's context. */
|
||||
blender::gpu::Texture *gpu_texture = nullptr;
|
||||
|
||||
/* Dimensions of the texture in pixels. */
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
protected:
|
||||
void reset()
|
||||
{
|
||||
gpu_texture = nullptr;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* Higher level representation of a Pixel Buffer Object (PBO) from the graphics library. */
|
||||
class DisplayGPUPixelBuffer {
|
||||
public:
|
||||
/* Global counter for all allocated blender::GPU module PBOs used by instances of this class. */
|
||||
static inline std::atomic<int> num_used = 0;
|
||||
|
||||
DisplayGPUPixelBuffer() = default;
|
||||
|
||||
~DisplayGPUPixelBuffer()
|
||||
{
|
||||
assert(gpu_pixel_buffer == nullptr);
|
||||
}
|
||||
|
||||
DisplayGPUPixelBuffer(const DisplayGPUPixelBuffer &other) = delete;
|
||||
DisplayGPUPixelBuffer &operator=(DisplayGPUPixelBuffer &other) = delete;
|
||||
|
||||
DisplayGPUPixelBuffer(DisplayGPUPixelBuffer &&other) noexcept
|
||||
: gpu_pixel_buffer(other.gpu_pixel_buffer), width(other.width), height(other.height)
|
||||
{
|
||||
other.reset();
|
||||
}
|
||||
|
||||
DisplayGPUPixelBuffer &operator=(DisplayGPUPixelBuffer &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
gpu_pixel_buffer = other.gpu_pixel_buffer;
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
|
||||
other.reset();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool gpu_resources_ensure(const uint new_width, const uint new_height, bool &buffer_recreated)
|
||||
{
|
||||
buffer_recreated = false;
|
||||
|
||||
const size_t required_size = sizeof(half4) * new_width * new_height;
|
||||
|
||||
/* Try to re-use the existing PBO if it has usable size. */
|
||||
if (gpu_pixel_buffer) {
|
||||
if (new_width != width || new_height != height ||
|
||||
blender::GPU_pixel_buffer_size(gpu_pixel_buffer) < required_size)
|
||||
{
|
||||
buffer_recreated = true;
|
||||
gpu_resources_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/* Update size. */
|
||||
width = new_width;
|
||||
height = new_height;
|
||||
|
||||
/* Create pixel buffer if not already created. */
|
||||
if (!gpu_pixel_buffer) {
|
||||
gpu_pixel_buffer = blender::GPU_pixel_buffer_create(required_size);
|
||||
buffer_recreated = true;
|
||||
}
|
||||
|
||||
if (gpu_pixel_buffer == nullptr) {
|
||||
LOG_ERROR << "Error creating texture pixel buffer object.";
|
||||
return false;
|
||||
}
|
||||
|
||||
++num_used;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
if (!gpu_pixel_buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
blender::GPU_pixel_buffer_free(gpu_pixel_buffer);
|
||||
gpu_pixel_buffer = nullptr;
|
||||
|
||||
reset();
|
||||
|
||||
--num_used;
|
||||
}
|
||||
|
||||
/* Pixel Buffer Object allocated by the blender::GPU module.
|
||||
*
|
||||
* NOTE: Allocated on the render engine's context. */
|
||||
blender::GPUPixelBuffer *gpu_pixel_buffer = nullptr;
|
||||
|
||||
/* Dimensions of the PBO. */
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
protected:
|
||||
void reset()
|
||||
{
|
||||
gpu_pixel_buffer = nullptr;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class DrawTile {
|
||||
public:
|
||||
DrawTile() = default;
|
||||
~DrawTile() = default;
|
||||
|
||||
DrawTile(const DrawTile &other) = delete;
|
||||
DrawTile &operator=(const DrawTile &other) = delete;
|
||||
|
||||
DrawTile(DrawTile &&other) noexcept = default;
|
||||
|
||||
DrawTile &operator=(DrawTile &&other) = default;
|
||||
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
texture.gpu_resources_destroy();
|
||||
}
|
||||
|
||||
bool ready_to_draw() const
|
||||
{
|
||||
return texture.gpu_texture != nullptr;
|
||||
}
|
||||
|
||||
/* Texture which contains pixels of the tile. */
|
||||
DisplayGPUTexture texture;
|
||||
|
||||
/* Display parameters the texture of this tile has been updated for. */
|
||||
BlenderDisplayDriver::Params params;
|
||||
};
|
||||
|
||||
class DrawTileAndPBO {
|
||||
public:
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
tile.gpu_resources_destroy();
|
||||
buffer_object.gpu_resources_destroy();
|
||||
}
|
||||
|
||||
DrawTile tile;
|
||||
DisplayGPUPixelBuffer buffer_object;
|
||||
bool need_update_texture_pixels = false;
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* BlenderDisplayDriver.
|
||||
*/
|
||||
|
||||
struct BlenderDisplayDriver::Tiles {
|
||||
/* Resources of a tile which is being currently rendered. */
|
||||
DrawTileAndPBO current_tile;
|
||||
|
||||
/* All tiles which rendering is finished and which content will not be changed. */
|
||||
struct {
|
||||
vector<DrawTile> tiles;
|
||||
|
||||
void gl_resources_destroy_and_clear()
|
||||
{
|
||||
for (DrawTile &tile : tiles) {
|
||||
tile.gpu_resources_destroy();
|
||||
}
|
||||
|
||||
tiles.clear();
|
||||
}
|
||||
} finished_tiles;
|
||||
};
|
||||
|
||||
BlenderDisplayDriver::BlenderDisplayDriver(blender::RenderEngine &b_engine,
|
||||
blender::Scene &b_scene,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const bool background)
|
||||
: b_engine_(b_engine),
|
||||
b_rv3d_(b_rv3d),
|
||||
background_(background),
|
||||
display_shader_(BlenderDisplayShader::create(b_engine, b_scene)),
|
||||
tiles_(make_unique<Tiles>())
|
||||
{
|
||||
/* Create context while on the main thread. */
|
||||
gpu_context_create();
|
||||
}
|
||||
|
||||
BlenderDisplayDriver::~BlenderDisplayDriver()
|
||||
{
|
||||
gpu_resources_destroy();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Update procedure.
|
||||
*/
|
||||
|
||||
void BlenderDisplayDriver::next_tile_begin()
|
||||
{
|
||||
if (!tiles_->current_tile.tile.ready_to_draw()) {
|
||||
LOG_ERROR
|
||||
<< "Unexpectedly moving to the next tile without any data provided for current tile.";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Moving to the next tile without giving render data for the current tile is not an expected
|
||||
* situation. */
|
||||
DCHECK(!need_zero_);
|
||||
/* Texture should have been updated from the PBO at this point. */
|
||||
DCHECK(!tiles_->current_tile.need_update_texture_pixels);
|
||||
|
||||
tiles_->finished_tiles.tiles.emplace_back(std::move(tiles_->current_tile.tile));
|
||||
}
|
||||
|
||||
bool BlenderDisplayDriver::update_begin(const Params ¶ms,
|
||||
const int texture_width,
|
||||
const int texture_height)
|
||||
{
|
||||
/* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing
|
||||
* the texture does not happen at the same time. This is achieved indirectly.
|
||||
*
|
||||
* When enabling the OpenGL/GPU context, it uses an internal mutex lock DST.gpu_context_lock.
|
||||
* This same lock is also held when do_draw() is called, which together ensure mutual
|
||||
* exclusion.
|
||||
*
|
||||
* This locking is not performed on the Cycles side, because that would cause lock inversion. */
|
||||
if (!gpu_context_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note: The render window might not draw between tiles. Wait for the previous
|
||||
* PBO-to-texture copy before reusing the PBO for the next tile. */
|
||||
blender::GPU_fence_wait(gpu_upload_sync_);
|
||||
blender::GPU_fence_wait(gpu_render_sync_);
|
||||
|
||||
DrawTile ¤t_tile = tiles_->current_tile.tile;
|
||||
DisplayGPUPixelBuffer ¤t_tile_buffer_object = tiles_->current_tile.buffer_object;
|
||||
|
||||
/* Clear storage of all finished tiles when display clear is requested.
|
||||
* Do it when new tile data is provided to handle the display clear flag in a single place.
|
||||
* It also makes the logic reliable from the whether drawing did happen or not point of view. */
|
||||
if (need_zero_) {
|
||||
tiles_->finished_tiles.gl_resources_destroy_and_clear();
|
||||
need_zero_ = false;
|
||||
}
|
||||
|
||||
/* Update PBO dimensions if needed.
|
||||
*
|
||||
* NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
|
||||
* at a resolution divider 1. This was we don't need to recreate graphics interoperability
|
||||
* objects which are costly and which are tied to the specific underlying buffer size.
|
||||
* The downside of this approach is that when graphics interoperability is not used we are
|
||||
* sending too much data to blender::GPU when resolution divider is not 1. */
|
||||
/* TODO(sergey): Investigate whether keeping the PBO exact size of the texture makes non-interop
|
||||
* mode faster. */
|
||||
const int buffer_width = params.size.x;
|
||||
const int buffer_height = params.size.y;
|
||||
bool interop_recreated = false;
|
||||
|
||||
if (!current_tile_buffer_object.gpu_resources_ensure(
|
||||
buffer_width, buffer_height, interop_recreated) ||
|
||||
!current_tile.texture.gpu_resources_ensure(texture_width, texture_height))
|
||||
{
|
||||
graphics_interop_buffer_.clear();
|
||||
tiles_->current_tile.gpu_resources_destroy();
|
||||
gpu_context_disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (interop_recreated) {
|
||||
graphics_interop_buffer_.clear();
|
||||
}
|
||||
|
||||
/* Store an updated parameters of the current tile.
|
||||
* In theory it is only needed once per update of the tile, but doing it on every update is
|
||||
* the easiest and is not expensive. */
|
||||
tiles_->current_tile.tile.params = params;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void update_tile_texture_pixels(const DrawTileAndPBO &tile)
|
||||
{
|
||||
const DisplayGPUTexture &texture = tile.tile.texture;
|
||||
|
||||
if (!DCHECK_NOTNULL(tile.buffer_object.gpu_pixel_buffer)) {
|
||||
LOG_ERROR << "Display driver tile pixel buffer unavailable.";
|
||||
return;
|
||||
}
|
||||
blender::GPU_texture_update_sub_from_pixel_buffer(texture.gpu_texture,
|
||||
blender::GPU_DATA_HALF_FLOAT,
|
||||
tile.buffer_object.gpu_pixel_buffer,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
texture.width,
|
||||
texture.height,
|
||||
0);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::update_end()
|
||||
{
|
||||
/* Unpack the PBO into the texture as soon as the new content is provided.
|
||||
*
|
||||
* This allows to ensure that the unpacking happens while resources like graphics interop (which
|
||||
* lifetime is outside of control of the display driver) are still valid, as well as allows to
|
||||
* move the tile from being current to finished blender::immediately after this call.
|
||||
*
|
||||
* One concern with this approach is that if the update happens more often than drawing then
|
||||
* doing the unpack here occupies blender::GPU transfer for no good reason. However, the render
|
||||
* scheduler takes care of ensuring updates don't happen that often. In regular applications
|
||||
* redraw will happen much more often than this update.
|
||||
*
|
||||
* On some older blender::GPUs on macOS, there is a driver crash when updating the texture for
|
||||
* viewport renders while Blender is drawing. As a workaround update texture during draw, under
|
||||
* assumption that there is no graphics interop on macOS and viewport render has a single tile.
|
||||
*/
|
||||
if (!background_ && blender::GPU_type_matches_ex(blender::GPU_DEVICE_NVIDIA,
|
||||
blender::GPU_OS_MAC,
|
||||
blender::GPU_DRIVER_ANY,
|
||||
blender::GPU_BACKEND_ANY))
|
||||
{
|
||||
tiles_->current_tile.need_update_texture_pixels = true;
|
||||
}
|
||||
else {
|
||||
update_tile_texture_pixels(tiles_->current_tile);
|
||||
}
|
||||
|
||||
/* Ensure blender::GPU fence exists to synchronize upload. */
|
||||
blender::GPU_fence_signal(gpu_upload_sync_);
|
||||
|
||||
blender::GPU_flush();
|
||||
|
||||
gpu_context_disable();
|
||||
|
||||
has_update_cond_.notify_all();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Texture buffer mapping.
|
||||
*/
|
||||
|
||||
half4 *BlenderDisplayDriver::map_texture_buffer()
|
||||
{
|
||||
/* With multi device rendering, Cycles can switch between using graphics interop
|
||||
* and not. For the denoised image it may be able to use graphics interop as that
|
||||
* buffer is written to by one device, while the noisy renders can not use it.
|
||||
*
|
||||
* We need to clear the graphics interop buffer on that switch, as blender::GPU_pixel_buffer_map
|
||||
* may recreate the buffer or handle. */
|
||||
graphics_interop_buffer_.clear();
|
||||
|
||||
blender::GPUPixelBuffer *pix_buf = tiles_->current_tile.buffer_object.gpu_pixel_buffer;
|
||||
if (!DCHECK_NOTNULL(pix_buf)) {
|
||||
LOG_ERROR << "Display driver tile pixel buffer unavailable.";
|
||||
return nullptr;
|
||||
}
|
||||
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(blender::GPU_pixel_buffer_map(pix_buf));
|
||||
if (!mapped_rgba_pixels) {
|
||||
LOG_ERROR << "Error mapping BlenderDisplayDriver pixel buffer object.";
|
||||
}
|
||||
return mapped_rgba_pixels;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::unmap_texture_buffer()
|
||||
{
|
||||
blender::GPUPixelBuffer *pix_buf = tiles_->current_tile.buffer_object.gpu_pixel_buffer;
|
||||
if (!DCHECK_NOTNULL(pix_buf)) {
|
||||
LOG_ERROR << "Display driver tile pixel buffer unavailable.";
|
||||
return;
|
||||
}
|
||||
blender::GPU_pixel_buffer_unmap(pix_buf);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Graphics interoperability.
|
||||
*/
|
||||
|
||||
GraphicsInteropDevice BlenderDisplayDriver::graphics_interop_get_device()
|
||||
{
|
||||
GraphicsInteropDevice interop_device;
|
||||
|
||||
switch (blender::GPU_backend_get_type()) {
|
||||
case blender::GPU_BACKEND_OPENGL:
|
||||
interop_device.type = GraphicsInteropDevice::OPENGL;
|
||||
break;
|
||||
case blender::GPU_BACKEND_VULKAN:
|
||||
interop_device.type = GraphicsInteropDevice::VULKAN;
|
||||
break;
|
||||
case blender::GPU_BACKEND_METAL:
|
||||
interop_device.type = GraphicsInteropDevice::METAL;
|
||||
break;
|
||||
case blender::GPU_BACKEND_NONE:
|
||||
case blender::GPU_BACKEND_ANY:
|
||||
interop_device.type = GraphicsInteropDevice::NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
blender::Span<uint8_t> uuid = blender::GPU_platform_uuid();
|
||||
interop_device.uuid.resize(uuid.size());
|
||||
std::copy_n(uuid.data(), uuid.size(), interop_device.uuid.data());
|
||||
|
||||
return interop_device;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::graphics_interop_update_buffer()
|
||||
{
|
||||
if (graphics_interop_buffer_.is_empty()) {
|
||||
GraphicsInteropDevice::Type type = GraphicsInteropDevice::NONE;
|
||||
switch (blender::GPU_backend_get_type()) {
|
||||
case blender::GPU_BACKEND_OPENGL:
|
||||
type = GraphicsInteropDevice::OPENGL;
|
||||
break;
|
||||
case blender::GPU_BACKEND_VULKAN:
|
||||
type = GraphicsInteropDevice::VULKAN;
|
||||
break;
|
||||
case blender::GPU_BACKEND_METAL:
|
||||
type = GraphicsInteropDevice::METAL;
|
||||
break;
|
||||
case blender::GPU_BACKEND_NONE:
|
||||
case blender::GPU_BACKEND_ANY:
|
||||
break;
|
||||
}
|
||||
|
||||
blender::GPUPixelBufferNativeHandle handle = blender::GPU_pixel_buffer_get_native_handle(
|
||||
tiles_->current_tile.buffer_object.gpu_pixel_buffer);
|
||||
graphics_interop_buffer_.assign(type, handle.handle, handle.size);
|
||||
}
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::graphics_interop_activate()
|
||||
{
|
||||
gpu_context_enable();
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::graphics_interop_deactivate()
|
||||
{
|
||||
gpu_context_disable();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Drawing.
|
||||
*/
|
||||
|
||||
void BlenderDisplayDriver::zero()
|
||||
{
|
||||
need_zero_ = true;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::set_zoom(const float zoom_x, const float zoom_y)
|
||||
{
|
||||
zoom_ = make_float2(zoom_x, zoom_y);
|
||||
}
|
||||
|
||||
/* Update vertex buffer with new coordinates of vertex positions and texture coordinates.
|
||||
* This buffer is used to render texture in the viewport.
|
||||
*
|
||||
* NOTE: The buffer needs to be bound. */
|
||||
static void vertex_draw(const DisplayDriver::Params ¶ms,
|
||||
const int texcoord_attribute,
|
||||
const int position_attribute)
|
||||
{
|
||||
const int x = params.full_offset.x;
|
||||
const int y = params.full_offset.y;
|
||||
|
||||
const int width = params.size.x;
|
||||
const int height = params.size.y;
|
||||
|
||||
blender::immBegin(blender::GPU_PRIM_TRI_STRIP, 4);
|
||||
|
||||
blender::immAttr2f(texcoord_attribute, 1.0f, 0.0f);
|
||||
blender::immVertex2f(position_attribute, x + width, y);
|
||||
|
||||
blender::immAttr2f(texcoord_attribute, 1.0f, 1.0f);
|
||||
blender::immVertex2f(position_attribute, x + width, y + height);
|
||||
|
||||
blender::immAttr2f(texcoord_attribute, 0.0f, 0.0f);
|
||||
blender::immVertex2f(position_attribute, x, y);
|
||||
|
||||
blender::immAttr2f(texcoord_attribute, 0.0f, 1.0f);
|
||||
blender::immVertex2f(position_attribute, x, y + height);
|
||||
|
||||
blender::immEnd();
|
||||
}
|
||||
|
||||
static void draw_tile(const float2 &zoom,
|
||||
const int texcoord_attribute,
|
||||
const int position_attribute,
|
||||
const DrawTile &draw_tile)
|
||||
{
|
||||
if (!draw_tile.ready_to_draw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DisplayGPUTexture &texture = draw_tile.texture;
|
||||
|
||||
if (!DCHECK_NOTNULL(texture.gpu_texture)) {
|
||||
LOG_ERROR << "Display driver tile blender::GPU texture resource unavailable.";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Trick to keep sharp rendering without jagged edges on all blender::GPUs.
|
||||
*
|
||||
* The idea here is to enforce driver to use linear interpolation when the image is zoomed out.
|
||||
* For the render result with a resolution divider in effect we always use nearest interpolation.
|
||||
*
|
||||
* Use explicit MIN assignment to make sure the driver does not have an undefined behavior at
|
||||
* the zoom level 1. The MAG filter is always NEAREST. */
|
||||
const float zoomed_width = draw_tile.params.size.x * zoom.x;
|
||||
const float zoomed_height = draw_tile.params.size.y * zoom.y;
|
||||
if (texture.width != draw_tile.params.size.x || texture.height != draw_tile.params.size.y) {
|
||||
/* Resolution divider is different from 1, force nearest interpolation. */
|
||||
blender::GPU_texture_bind_ex(
|
||||
texture.gpu_texture, blender::GPUSamplerState::default_sampler(), 0);
|
||||
}
|
||||
else if (zoomed_width - draw_tile.params.size.x > -0.5f ||
|
||||
zoomed_height - draw_tile.params.size.y > -0.5f)
|
||||
{
|
||||
blender::GPU_texture_bind_ex(
|
||||
texture.gpu_texture, blender::GPUSamplerState::default_sampler(), 0);
|
||||
}
|
||||
else {
|
||||
blender::GPU_texture_bind_ex(texture.gpu_texture, {blender::GPU_SAMPLER_FILTERING_LINEAR}, 0);
|
||||
}
|
||||
|
||||
/* Draw at the parameters for which the texture has been updated for. This allows to always draw
|
||||
* texture during bordered-rendered camera view without flickering. The validness of the display
|
||||
* parameters for a texture is guaranteed by the initial "clear" state which makes drawing to
|
||||
* have an early output.
|
||||
*
|
||||
* Such approach can cause some extra "jelly" effect during panning, but it is not more jelly
|
||||
* than overlay of selected objects. Also, it's possible to redraw texture at an intersection of
|
||||
* the texture draw parameters and the latest updated draw parameters (although, complexity of
|
||||
* doing it might not worth it. */
|
||||
vertex_draw(draw_tile.params, texcoord_attribute, position_attribute);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::flush()
|
||||
{
|
||||
/* This is called from the render thread that also calls update_begin/end, right before ending
|
||||
* the render loop. We wait for any queued PBO and render commands to be done, before destroying
|
||||
* the render thread and activating the context in the main thread to destroy resources.
|
||||
*
|
||||
* If we don't do this, the NVIDIA driver hangs for a few seconds for when ending 3D viewport
|
||||
* rendering, for unknown reasons. This was found with NVIDIA driver version 470.73 and a Quadro
|
||||
* RTX 6000 on Linux. */
|
||||
if (!gpu_context_enable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blender::GPU_fence_wait(gpu_upload_sync_);
|
||||
blender::GPU_fence_wait(gpu_render_sync_);
|
||||
|
||||
gpu_context_disable();
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::draw(const Params ¶ms)
|
||||
{
|
||||
if (b_rv3d_ && (b_rv3d_->rflag & (blender::RV3D_NAVIGATING | blender::RV3D_PAINTING))) {
|
||||
/* Before drawing, wait that an update to the texture has actually occurred, to synchronize
|
||||
* rendering of Cycles with Blender. Use a timeout to prevent user interface in the main thread
|
||||
* from becoming unresponsive when rendering is too heavy. */
|
||||
thread_scoped_lock lock(has_update_mutex_);
|
||||
has_update_cond_.wait_for(lock, std::chrono::milliseconds(33));
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
gpu_context_lock();
|
||||
|
||||
if (need_zero_) {
|
||||
/* Texture is requested to be cleared and was not yet cleared.
|
||||
*
|
||||
* Do early return which should be equivalent of drawing all-zero texture.
|
||||
* Watch out for the lock though so that the clear happening during update is properly
|
||||
* synchronized here. */
|
||||
gpu_context_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
blender::GPU_fence_wait(gpu_upload_sync_);
|
||||
blender::GPU_blend(blender::GPU_BLEND_ALPHA_PREMULT);
|
||||
|
||||
blender::gpu::Shader *active_shader = display_shader_->bind(params.full_size.x,
|
||||
params.full_size.y);
|
||||
|
||||
blender::GPUVertFormat *format = blender::immVertexFormat();
|
||||
const int texcoord_attribute = blender::GPU_vertformat_attr_add(
|
||||
format,
|
||||
ccl::BlenderDisplayShader::tex_coord_attribute_name,
|
||||
blender::gpu::VertAttrType::SFLOAT_32_32);
|
||||
const int position_attribute = blender::GPU_vertformat_attr_add(
|
||||
format,
|
||||
ccl::BlenderDisplayShader::position_attribute_name,
|
||||
blender::gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
/* NOTE: Shader is bound again through IMM to register this shader with the IMM module
|
||||
* and perform required setup for IMM rendering. This is required as the IMM module
|
||||
* needs to be aware of which shader is bound, and the main display shader
|
||||
* is bound externally. */
|
||||
blender::immBindShader(active_shader);
|
||||
|
||||
if (tiles_->current_tile.need_update_texture_pixels) {
|
||||
update_tile_texture_pixels(tiles_->current_tile);
|
||||
tiles_->current_tile.need_update_texture_pixels = false;
|
||||
}
|
||||
|
||||
draw_tile(zoom_, texcoord_attribute, position_attribute, tiles_->current_tile.tile);
|
||||
|
||||
for (const DrawTile &tile : tiles_->finished_tiles.tiles) {
|
||||
draw_tile(zoom_, texcoord_attribute, position_attribute, tile);
|
||||
}
|
||||
|
||||
/* Reset IMM shader bind state. */
|
||||
blender::immUnbindProgram();
|
||||
|
||||
display_shader_->unbind();
|
||||
|
||||
blender::GPU_blend(blender::GPU_BLEND_NONE);
|
||||
|
||||
blender::GPU_fence_signal(gpu_render_sync_);
|
||||
blender::GPU_flush();
|
||||
|
||||
gpu_context_unlock();
|
||||
|
||||
LOG_TRACE << "Display driver number of textures: " << DisplayGPUTexture::num_used;
|
||||
LOG_TRACE << "Display driver number of PBOs: " << DisplayGPUPixelBuffer::num_used;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_context_create()
|
||||
{
|
||||
if (!RE_engine_gpu_context_create(&b_engine_)) {
|
||||
LOG_ERROR << "Error creating blender::GPU context.";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create global blender::GPU resources for display driver. */
|
||||
if (!gpu_resources_create()) {
|
||||
LOG_ERROR << "Error creating blender::GPU resources for Display Driver.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderDisplayDriver::gpu_context_enable()
|
||||
{
|
||||
return RE_engine_gpu_context_enable(&b_engine_);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_context_disable()
|
||||
{
|
||||
RE_engine_gpu_context_disable(&b_engine_);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_context_destroy()
|
||||
{
|
||||
RE_engine_gpu_context_destroy(&b_engine_);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_context_lock()
|
||||
{
|
||||
RE_engine_gpu_context_lock(&b_engine_);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_context_unlock()
|
||||
{
|
||||
RE_engine_gpu_context_unlock(&b_engine_);
|
||||
}
|
||||
|
||||
bool BlenderDisplayDriver::gpu_resources_create()
|
||||
{
|
||||
/* Ensure context is active for resource creation. */
|
||||
if (!gpu_context_enable()) {
|
||||
LOG_ERROR << "Error enabling blender::GPU context.";
|
||||
return false;
|
||||
}
|
||||
|
||||
gpu_upload_sync_ = blender::GPU_fence_create();
|
||||
gpu_render_sync_ = blender::GPU_fence_create();
|
||||
|
||||
if (!DCHECK_NOTNULL(gpu_upload_sync_) || !DCHECK_NOTNULL(gpu_render_sync_)) {
|
||||
LOG_ERROR << "Error creating blender::GPU synchronization primitives.";
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
gpu_context_disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_resources_destroy()
|
||||
{
|
||||
gpu_context_enable();
|
||||
|
||||
display_shader_.reset();
|
||||
|
||||
graphics_interop_buffer_.clear();
|
||||
|
||||
tiles_->current_tile.gpu_resources_destroy();
|
||||
tiles_->finished_tiles.gl_resources_destroy_and_clear();
|
||||
|
||||
/* Fences. */
|
||||
if (gpu_render_sync_) {
|
||||
blender::GPU_fence_free(gpu_render_sync_);
|
||||
gpu_render_sync_ = nullptr;
|
||||
}
|
||||
if (gpu_upload_sync_) {
|
||||
blender::GPU_fence_free(gpu_upload_sync_);
|
||||
gpu_upload_sync_ = nullptr;
|
||||
}
|
||||
|
||||
gpu_context_disable();
|
||||
|
||||
gpu_context_destroy();
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
144
blender-5.2.0/intern/cycles/blender/display_driver.h
Normal file
144
blender-5.2.0/intern/cycles/blender/display_driver.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "session/display_driver.h"
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "util/unique_ptr.h"
|
||||
|
||||
namespace blender {
|
||||
struct GPUContext;
|
||||
struct GPUFence;
|
||||
struct RenderEngine;
|
||||
struct Scene;
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Base class of shader used for display driver rendering. */
|
||||
class BlenderDisplayShader {
|
||||
public:
|
||||
static constexpr const char *position_attribute_name = "pos";
|
||||
static constexpr const char *tex_coord_attribute_name = "texCoord";
|
||||
|
||||
/* Create shader implementation suitable for the given render engine and scene configuration. */
|
||||
static unique_ptr<BlenderDisplayShader> create(blender::RenderEngine &b_engine,
|
||||
blender::Scene &b_scene);
|
||||
|
||||
BlenderDisplayShader() = default;
|
||||
virtual ~BlenderDisplayShader() = default;
|
||||
|
||||
virtual blender::gpu::Shader *bind(const int width, const int height) = 0;
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/* Get attribute location for position and texture coordinate respectively.
|
||||
* NOTE: The shader needs to be bound to have access to those. */
|
||||
virtual int get_position_attrib_location();
|
||||
virtual int get_tex_coord_attrib_location();
|
||||
|
||||
protected:
|
||||
/* Get program of this display shader.
|
||||
* NOTE: The shader needs to be bound to have access to this. */
|
||||
virtual blender::gpu::Shader *get_shader_program() = 0;
|
||||
|
||||
/* Cached values of various OpenGL resources. */
|
||||
int position_attribute_location_ = -1;
|
||||
int tex_coord_attribute_location_ = -1;
|
||||
};
|
||||
|
||||
class BlenderDisplaySpaceShader : public BlenderDisplayShader {
|
||||
public:
|
||||
BlenderDisplaySpaceShader(blender::RenderEngine &b_engine, blender::Scene &b_scene);
|
||||
|
||||
blender::gpu::Shader *bind(const int width, const int height) override;
|
||||
void unbind() override;
|
||||
|
||||
protected:
|
||||
blender::gpu::Shader *get_shader_program() override;
|
||||
|
||||
blender::RenderEngine &b_engine_;
|
||||
blender::Scene &b_scene_;
|
||||
|
||||
/* Cached values of various OpenGL resources. */
|
||||
blender::gpu::Shader *shader_program_ = nullptr;
|
||||
};
|
||||
|
||||
/* Display driver implementation which is specific for Blender viewport integration. */
|
||||
class BlenderDisplayDriver : public DisplayDriver {
|
||||
public:
|
||||
BlenderDisplayDriver(blender::RenderEngine &b_engine,
|
||||
blender::Scene &b_scene,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const bool background);
|
||||
~BlenderDisplayDriver() override;
|
||||
|
||||
void graphics_interop_activate() override;
|
||||
void graphics_interop_deactivate() override;
|
||||
|
||||
void zero() override;
|
||||
|
||||
void set_zoom(const float zoom_x, const float zoom_y);
|
||||
|
||||
protected:
|
||||
void next_tile_begin() override;
|
||||
|
||||
bool update_begin(const Params ¶ms,
|
||||
const int texture_width,
|
||||
const int texture_height) override;
|
||||
void update_end() override;
|
||||
|
||||
half4 *map_texture_buffer() override;
|
||||
void unmap_texture_buffer() override;
|
||||
|
||||
GraphicsInteropDevice graphics_interop_get_device() override;
|
||||
void graphics_interop_update_buffer() override;
|
||||
|
||||
void draw(const Params ¶ms) override;
|
||||
|
||||
void flush() override;
|
||||
|
||||
/* Helper function which allocates new GPU context. */
|
||||
void gpu_context_create();
|
||||
bool gpu_context_enable();
|
||||
void gpu_context_disable();
|
||||
void gpu_context_destroy();
|
||||
void gpu_context_lock();
|
||||
void gpu_context_unlock();
|
||||
|
||||
/* Create GPU resources used by the display driver. */
|
||||
bool gpu_resources_create();
|
||||
|
||||
/* Destroy all GPU resources which are being used by this object. */
|
||||
void gpu_resources_destroy();
|
||||
|
||||
blender::RenderEngine &b_engine_;
|
||||
blender::RegionView3D *b_rv3d_;
|
||||
bool background_;
|
||||
|
||||
/* Content of the display is to be filled with zeroes. */
|
||||
std::atomic<bool> need_zero_ = true;
|
||||
|
||||
unique_ptr<BlenderDisplayShader> display_shader_;
|
||||
|
||||
/* Opaque storage for an internal state and data for tiles. */
|
||||
struct Tiles;
|
||||
unique_ptr<Tiles> tiles_;
|
||||
|
||||
blender::GPUFence *gpu_render_sync_ = nullptr;
|
||||
blender::GPUFence *gpu_upload_sync_ = nullptr;
|
||||
|
||||
float2 zoom_ = make_float2(1.0f, 1.0f);
|
||||
|
||||
thread_condition_variable has_update_cond_;
|
||||
thread_mutex has_update_mutex_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
312
blender-5.2.0/intern/cycles/blender/geometry.cpp
Normal file
312
blender-5.2.0/intern/cycles/blender/geometry.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/curves.h"
|
||||
#include "scene/hair.h"
|
||||
#include "scene/light.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/volume.h"
|
||||
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "util/task.h"
|
||||
|
||||
#include "BKE_material.hh"
|
||||
#include "DNA_light_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair)
|
||||
{
|
||||
if (GS(b_ob_info.object_data->name) == blender::ID_LA) {
|
||||
blender::Light &b_light = *blender::id_cast<blender::Light *>(b_ob_info.object_data);
|
||||
switch (b_light.type) {
|
||||
case blender::LA_LOCAL:
|
||||
return Geometry::POINT_LIGHT;
|
||||
case blender::LA_SPOT:
|
||||
return Geometry::SPOT_LIGHT;
|
||||
case blender::LA_SUN:
|
||||
return Geometry::SUN_LIGHT;
|
||||
case blender::LA_AREA:
|
||||
return Geometry::AREA_LIGHT;
|
||||
default:
|
||||
/* Should be handled in `sync_background_light()`. */
|
||||
assert(false);
|
||||
return Geometry::BACKGROUND_LIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
if (GS(b_ob_info.object_data->name) == blender::ID_CV || use_particle_hair) {
|
||||
return Geometry::HAIR;
|
||||
}
|
||||
|
||||
if (GS(b_ob_info.object_data->name) == blender::ID_PT) {
|
||||
return Geometry::POINTCLOUD;
|
||||
}
|
||||
|
||||
if (GS(b_ob_info.object_data->name) == blender::ID_VO ||
|
||||
(b_ob_info.object_data ==
|
||||
object_get_data(*b_ob_info.real_object, b_ob_info.use_adaptive_subdivision) &&
|
||||
object_fluid_gas_domain_find(*b_ob_info.real_object)))
|
||||
{
|
||||
return Geometry::VOLUME;
|
||||
}
|
||||
|
||||
return Geometry::MESH;
|
||||
}
|
||||
|
||||
array<Node *> BlenderSync::find_used_shaders(blender::Object &b_ob)
|
||||
{
|
||||
array<Node *> used_shaders;
|
||||
|
||||
if (b_ob.type == blender::OB_LAMP) {
|
||||
find_shader(static_cast<blender::ID *>(b_ob.data), used_shaders, scene->default_light);
|
||||
return used_shaders;
|
||||
}
|
||||
|
||||
blender::Material *material_override = view_layer.material_override;
|
||||
Shader *default_shader = (b_ob.type == blender::OB_VOLUME) ? scene->default_volume :
|
||||
scene->default_surface;
|
||||
|
||||
for (const int i : blender::IndexRange(BKE_object_material_count_eval(&b_ob))) {
|
||||
if (material_override) {
|
||||
find_shader(&material_override->id, used_shaders, default_shader);
|
||||
}
|
||||
else {
|
||||
blender::Material *b_material = BKE_object_material_get(&b_ob, i + 1);
|
||||
find_shader(reinterpret_cast<blender::ID *>(b_material), used_shaders, default_shader);
|
||||
}
|
||||
}
|
||||
|
||||
if (used_shaders.size() == 0) {
|
||||
if (material_override) {
|
||||
find_shader(&material_override->id, used_shaders, default_shader);
|
||||
}
|
||||
else {
|
||||
used_shaders.push_back_slow(default_shader);
|
||||
}
|
||||
}
|
||||
|
||||
return used_shaders;
|
||||
}
|
||||
|
||||
Geometry *BlenderSync::sync_geometry(BObjectInfo &b_ob_info,
|
||||
bool object_updated,
|
||||
bool use_particle_hair,
|
||||
TaskPool *task_pool)
|
||||
{
|
||||
/* Test if we can instance or if the object is modified. */
|
||||
const Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair);
|
||||
blender::ID *const b_key_id = (b_ob_info.is_real_object_data() &&
|
||||
BKE_object_is_modified(*b_ob_info.real_object)) ?
|
||||
&b_ob_info.real_object->id :
|
||||
b_ob_info.object_data;
|
||||
const GeometryKey key(b_key_id, geom_type);
|
||||
|
||||
/* Find shader indices. */
|
||||
array<Node *> used_shaders = find_used_shaders(*b_ob_info.iter_object);
|
||||
|
||||
/* Ensure we only sync instanced geometry once. */
|
||||
Geometry *geom = geometry_map.find(key);
|
||||
if (geom) {
|
||||
if (geometry_synced.contains(geom)) {
|
||||
return geom;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test if we need to sync. */
|
||||
bool sync = true;
|
||||
if (geom == nullptr) {
|
||||
/* Add new geometry if it did not exist yet. */
|
||||
if (geom_type == Geometry::POINT_LIGHT) {
|
||||
geom = scene->create_light_node<PointLight>();
|
||||
}
|
||||
else if (geom_type == Geometry::SPOT_LIGHT) {
|
||||
geom = scene->create_light_node<SpotLight>();
|
||||
}
|
||||
else if (geom_type == Geometry::SUN_LIGHT) {
|
||||
geom = scene->create_light_node<SunLight>();
|
||||
}
|
||||
else if (geom_type == Geometry::AREA_LIGHT) {
|
||||
geom = scene->create_light_node<AreaLight>();
|
||||
}
|
||||
else if (geom_type == Geometry::HAIR) {
|
||||
geom = scene->create_node<Hair>();
|
||||
}
|
||||
else if (geom_type == Geometry::VOLUME) {
|
||||
geom = scene->create_node<Volume>();
|
||||
}
|
||||
else if (geom_type == Geometry::POINTCLOUD) {
|
||||
geom = scene->create_node<PointCloud>();
|
||||
}
|
||||
else {
|
||||
assert(geom_type == Geometry::MESH);
|
||||
geom = scene->create_node<Mesh>();
|
||||
}
|
||||
geometry_map.add(key, geom);
|
||||
}
|
||||
else {
|
||||
/* Test if we need to update existing geometry. */
|
||||
sync = geometry_map.update(geom, b_key_id);
|
||||
}
|
||||
|
||||
if (!sync) {
|
||||
/* If transform was applied to geometry, need full update. */
|
||||
if (object_updated && geom->transform_applied) {
|
||||
;
|
||||
}
|
||||
/* Test if shaders changed, these can be object level so geometry
|
||||
* does not get tagged for recalc. */
|
||||
else if (geom->get_used_shaders() != used_shaders) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
/* Even if not tagged for recalc, we may need to sync anyway
|
||||
* because the shader needs different geometry attributes. */
|
||||
bool attribute_recalc = false;
|
||||
|
||||
for (Node *node : geom->get_used_shaders()) {
|
||||
Shader *shader = static_cast<Shader *>(node);
|
||||
if (shader->need_update_geometry()) {
|
||||
attribute_recalc = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!attribute_recalc) {
|
||||
return geom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geometry_synced.insert(geom);
|
||||
|
||||
geom->name = ustring(BKE_id_name(*b_ob_info.object_data));
|
||||
|
||||
/* Store the shaders immediately for the object attribute code. */
|
||||
geom->set_used_shaders(used_shaders);
|
||||
|
||||
auto sync_func = [this, geom_type, b_ob_info, geom]() mutable {
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
progress.set_sync_status("Synchronizing object", BKE_id_name(b_ob_info.real_object->id));
|
||||
|
||||
if (geom->is_light()) {
|
||||
Light *light = static_cast<Light *>(geom);
|
||||
sync_light(b_ob_info, light);
|
||||
}
|
||||
else if (geom_type == Geometry::HAIR) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
sync_hair(b_ob_info, hair);
|
||||
}
|
||||
else if (geom_type == Geometry::VOLUME) {
|
||||
Volume *volume = static_cast<Volume *>(geom);
|
||||
sync_volume(b_ob_info, volume);
|
||||
}
|
||||
else if (geom_type == Geometry::POINTCLOUD) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
sync_pointcloud(pointcloud, b_ob_info);
|
||||
}
|
||||
else {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
sync_mesh(b_ob_info, mesh);
|
||||
}
|
||||
};
|
||||
|
||||
/* Defer the actual geometry sync to the task_pool for multithreading */
|
||||
if (task_pool) {
|
||||
task_pool->push(sync_func);
|
||||
}
|
||||
else {
|
||||
sync_func();
|
||||
}
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
void BlenderSync::sync_geometry_motion(BObjectInfo &b_ob_info,
|
||||
Object *object,
|
||||
const float motion_time,
|
||||
bool use_particle_hair,
|
||||
TaskPool *task_pool)
|
||||
{
|
||||
/* Ensure we only sync instanced geometry once. */
|
||||
Geometry *geom = object->get_geometry();
|
||||
|
||||
if (geometry_motion_synced.contains(geom) || geometry_motion_attribute_synced.contains(geom)) {
|
||||
return;
|
||||
}
|
||||
|
||||
geometry_motion_synced.insert(geom);
|
||||
|
||||
/* Ensure we only motion sync geometry that also had geometry synced, to avoid
|
||||
* unnecessary work and to ensure that its attributes were clear. */
|
||||
if (!geometry_synced.contains(geom)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing to do for lights. */
|
||||
if (geom->is_light()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the geometry already has motion blur from a velocity attribute, don't
|
||||
* set the geometry motion steps again.
|
||||
*
|
||||
* Otherwise, setting geometry motion steps is done here to avoid concurrency issues.
|
||||
* - It can't be done earlier in sync_object_motion_init because sync_geometry
|
||||
* runs in parallel, and has_motion_blur would check attributes while
|
||||
* sync_geometry is potentially creating the attribute from velocity.
|
||||
* - It needs to happen before the parallel motion sync that happens right after
|
||||
* this, because that can create the attribute from neighboring frames.
|
||||
* Copying the motion steps from the object here solves this. */
|
||||
if (!geom->has_motion_blur()) {
|
||||
geom->set_motion_steps(object->get_motion().size());
|
||||
}
|
||||
|
||||
/* Find time matching motion step required by geometry. */
|
||||
const int motion_step = geom->motion_step(motion_time);
|
||||
if (motion_step < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto sync_func = [this, b_ob_info, use_particle_hair, motion_step, geom]() mutable {
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GS(b_ob_info.object_data->name) == blender::ID_CV || use_particle_hair) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
sync_hair_motion(b_ob_info, hair, motion_step);
|
||||
}
|
||||
else if (GS(b_ob_info.object_data->name) == blender::ID_VO ||
|
||||
object_fluid_gas_domain_find(*b_ob_info.real_object))
|
||||
{
|
||||
/* No volume motion blur support yet. */
|
||||
}
|
||||
else if (GS(b_ob_info.object_data->name) == blender::ID_PT) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
sync_pointcloud_motion(pointcloud, b_ob_info, motion_step);
|
||||
}
|
||||
else {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
sync_mesh_motion(b_ob_info, mesh, motion_step);
|
||||
}
|
||||
};
|
||||
|
||||
/* Defer the actual geometry sync to the task_pool for multithreading */
|
||||
if (task_pool) {
|
||||
task_pool->push(sync_func);
|
||||
}
|
||||
else {
|
||||
sync_func();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
313
blender-5.2.0/intern/cycles/blender/id_map.h
Normal file
313
blender-5.2.0/intern/cycles/blender/id_map.h
Normal file
@@ -0,0 +1,313 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "scene/geometry.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#include "util/map.h"
|
||||
#include "util/set.h"
|
||||
|
||||
namespace blender {
|
||||
struct ID;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* ID Map
|
||||
*
|
||||
* Utility class to map between Blender datablocks and Cycles data structures,
|
||||
* and keep track of recalc tags from the dependency graph. */
|
||||
|
||||
template<typename K, typename T, typename Flags = uint> class id_map {
|
||||
public:
|
||||
id_map(Scene *scene_) : scene(scene_) {}
|
||||
|
||||
~id_map()
|
||||
{
|
||||
set<T *> nodes;
|
||||
|
||||
typename map<K, T *>::iterator jt;
|
||||
for (jt = b_map.begin(); jt != b_map.end(); jt++) {
|
||||
nodes.insert(jt->second);
|
||||
}
|
||||
|
||||
scene->delete_nodes(nodes);
|
||||
}
|
||||
|
||||
T *find(const K &key)
|
||||
{
|
||||
if (b_map.find(key) != b_map.end()) {
|
||||
T *data = b_map[key];
|
||||
return data;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void set_recalc(void *id_ptr)
|
||||
{
|
||||
b_recalc.insert(id_ptr);
|
||||
}
|
||||
|
||||
bool check_recalc(const blender::ID *id)
|
||||
{
|
||||
return id && b_recalc.contains(id);
|
||||
}
|
||||
|
||||
bool has_recalc()
|
||||
{
|
||||
return !(b_recalc.empty());
|
||||
}
|
||||
|
||||
void pre_sync()
|
||||
{
|
||||
used_set.clear();
|
||||
}
|
||||
|
||||
/* Add new data. */
|
||||
void add(const K &key, T *data)
|
||||
{
|
||||
assert(find(key) == nullptr);
|
||||
b_map[key] = data;
|
||||
used(data);
|
||||
}
|
||||
|
||||
/* Update existing data. */
|
||||
bool update(T *data, const blender::ID *id)
|
||||
{
|
||||
return update(data, id, id);
|
||||
}
|
||||
bool update(T *data, const blender::ID *id, const blender::ID *parent)
|
||||
{
|
||||
bool recalc = (b_recalc.contains(id));
|
||||
if (parent && parent != id) {
|
||||
recalc = recalc || (b_recalc.contains(parent));
|
||||
}
|
||||
used(data);
|
||||
return recalc;
|
||||
}
|
||||
|
||||
/* Combined add and update as needed. */
|
||||
bool add_or_update(T **r_data, const blender::ID *id)
|
||||
{
|
||||
return add_or_update(r_data, id, id, id);
|
||||
}
|
||||
bool add_or_update(T **r_data, const blender::ID *id, const K &key)
|
||||
{
|
||||
return add_or_update(r_data, id, id, key);
|
||||
}
|
||||
bool add_or_update(T **r_data, const blender::ID *id, const blender::ID *parent, const K &key)
|
||||
{
|
||||
T *data = find(key);
|
||||
bool recalc;
|
||||
|
||||
if (!data) {
|
||||
/* Add data if it didn't exist yet. */
|
||||
data = scene->create_node<T>();
|
||||
add(key, data);
|
||||
recalc = true;
|
||||
}
|
||||
else {
|
||||
/* check if updated needed. */
|
||||
recalc = update(data, id, parent);
|
||||
}
|
||||
|
||||
*r_data = data;
|
||||
return recalc;
|
||||
}
|
||||
|
||||
/* Combined add or update for convenience. */
|
||||
|
||||
bool is_used(const K &key)
|
||||
{
|
||||
T *data = find(key);
|
||||
return (data) ? used_set.find(data) != used_set.end() : false;
|
||||
}
|
||||
|
||||
void used(T *data)
|
||||
{
|
||||
/* tag data as still in use */
|
||||
used_set.insert(data);
|
||||
}
|
||||
|
||||
void set_default(T *data)
|
||||
{
|
||||
b_map[nullptr] = data;
|
||||
}
|
||||
|
||||
void post_sync(bool do_delete = true)
|
||||
{
|
||||
map<K, T *> new_map;
|
||||
set<T *> nodes_to_delete;
|
||||
using TMapPair = pair<const K, T *>;
|
||||
typename map<K, T *>::iterator jt;
|
||||
|
||||
for (jt = b_map.begin(); jt != b_map.end(); jt++) {
|
||||
TMapPair &pair = *jt;
|
||||
|
||||
if (do_delete && used_set.find(pair.second) == used_set.end()) {
|
||||
flags.erase(pair.second);
|
||||
nodes_to_delete.insert(pair.second);
|
||||
}
|
||||
else {
|
||||
new_map[pair.first] = pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nodes_to_delete.empty()) {
|
||||
scene->delete_nodes(nodes_to_delete);
|
||||
}
|
||||
|
||||
used_set.clear();
|
||||
b_recalc.clear();
|
||||
b_map = new_map;
|
||||
}
|
||||
|
||||
const map<K, T *> &key_to_scene_data()
|
||||
{
|
||||
return b_map;
|
||||
}
|
||||
|
||||
bool test_flag(T *data, Flags val)
|
||||
{
|
||||
typename map<T *, uint>::iterator it = flags.find(data);
|
||||
return it != flags.end() && (it->second & (1 << val)) != 0;
|
||||
}
|
||||
|
||||
void set_flag(T *data, Flags val)
|
||||
{
|
||||
flags[data] |= (1 << val);
|
||||
}
|
||||
|
||||
void clear_flag(T *data, Flags val)
|
||||
{
|
||||
typename map<T *, uint>::iterator it = flags.find(data);
|
||||
if (it != flags.end()) {
|
||||
it->second &= ~(1 << val);
|
||||
|
||||
if (it->second == 0) {
|
||||
flags.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
map<K, T *> b_map;
|
||||
set<T *> used_set;
|
||||
map<T *, uint> flags;
|
||||
set<const void *> b_recalc;
|
||||
Scene *scene;
|
||||
};
|
||||
|
||||
/* Object Key
|
||||
*
|
||||
* To uniquely identify instances, we use the parent, object and persistent instance ID.
|
||||
* We also export separate object for a mesh and its particle hair. */
|
||||
|
||||
enum { OBJECT_PERSISTENT_ID_SIZE = 8 /* MAX_DUPLI_RECUR in Blender. */ };
|
||||
|
||||
struct ObjectKey {
|
||||
void *parent;
|
||||
int id[OBJECT_PERSISTENT_ID_SIZE];
|
||||
void *ob;
|
||||
bool use_particle_hair;
|
||||
|
||||
ObjectKey(void *parent_,
|
||||
const int id_[OBJECT_PERSISTENT_ID_SIZE],
|
||||
void *ob_,
|
||||
bool use_particle_hair_)
|
||||
: parent(parent_), ob(ob_), use_particle_hair(use_particle_hair_)
|
||||
{
|
||||
if (id_) {
|
||||
memcpy(id, id_, sizeof(id));
|
||||
}
|
||||
else {
|
||||
memset(id, 0, sizeof(id));
|
||||
}
|
||||
}
|
||||
|
||||
bool operator<(const ObjectKey &k) const
|
||||
{
|
||||
if (ob < k.ob) {
|
||||
return true;
|
||||
}
|
||||
if (ob == k.ob) {
|
||||
if (parent < k.parent) {
|
||||
return true;
|
||||
}
|
||||
if (parent == k.parent) {
|
||||
if (use_particle_hair < k.use_particle_hair) {
|
||||
return true;
|
||||
}
|
||||
if (use_particle_hair == k.use_particle_hair) {
|
||||
return memcmp(id, k.id, sizeof(id)) < 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/* Geometry Key
|
||||
*
|
||||
* We export separate geometry for a mesh and its particle hair, so key needs to
|
||||
* distinguish between them. */
|
||||
|
||||
struct GeometryKey {
|
||||
void *id;
|
||||
Geometry::Type geometry_type;
|
||||
|
||||
GeometryKey(void *id, Geometry::Type geometry_type) : id(id), geometry_type(geometry_type) {}
|
||||
|
||||
bool operator<(const GeometryKey &k) const
|
||||
{
|
||||
if (id < k.id) {
|
||||
return true;
|
||||
}
|
||||
if (id == k.id) {
|
||||
if (geometry_type < k.geometry_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/* Particle System Key */
|
||||
|
||||
struct ParticleSystemKey {
|
||||
void *ob;
|
||||
int id[OBJECT_PERSISTENT_ID_SIZE];
|
||||
|
||||
ParticleSystemKey(void *ob_, const int id_[OBJECT_PERSISTENT_ID_SIZE]) : ob(ob_)
|
||||
{
|
||||
if (id_) {
|
||||
memcpy(id, id_, sizeof(id));
|
||||
}
|
||||
else {
|
||||
memset(id, 0, sizeof(id));
|
||||
}
|
||||
}
|
||||
|
||||
bool operator<(const ParticleSystemKey &k) const
|
||||
{
|
||||
/* first id is particle index, we don't compare that */
|
||||
if (ob < k.ob) {
|
||||
return true;
|
||||
}
|
||||
if (ob == k.ob) {
|
||||
return memcmp(id + 1, k.id + 1, sizeof(int) * (OBJECT_PERSISTENT_ID_SIZE - 1)) < 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
262
blender-5.2.0/intern/cycles/blender/image.cpp
Normal file
262
blender-5.2.0/intern/cycles/blender/image.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
|
||||
#include "blender/image.h"
|
||||
#include "blender/session.h"
|
||||
|
||||
#include "util/half.h"
|
||||
#include "util/types_float4.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Packed Images */
|
||||
|
||||
BlenderImageLoader::BlenderImageLoader(blender::Image *b_image,
|
||||
blender::ImageUser *b_iuser,
|
||||
const int frame,
|
||||
const int tile_number,
|
||||
const bool is_preview_render)
|
||||
: b_image(b_image),
|
||||
b_iuser(*b_iuser),
|
||||
/* Don't free cache for preview render to avoid race condition from #93560, to be fixed
|
||||
* properly later as we are close to release. */
|
||||
free_cache(!is_preview_render && !BKE_image_has_loaded_ibuf(b_image)),
|
||||
cached_update_count(b_image->runtime->update_count)
|
||||
{
|
||||
this->b_iuser.framenr = frame;
|
||||
if (b_image->source != blender::IMA_SRC_TILED) {
|
||||
/* Image sequences currently not supported by this image loader. */
|
||||
assert(b_image->source != blender::IMA_SRC_SEQUENCE);
|
||||
}
|
||||
else {
|
||||
/* Set UDIM tile, each can have different resolution. */
|
||||
this->b_iuser.tile = tile_number;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderImageLoader::load_metadata(ImageMetaData &metadata,
|
||||
const ImageLoaderParams & /*params*/,
|
||||
Progress & /*progress*/)
|
||||
{
|
||||
bool is_float = false;
|
||||
bool is_data = false;
|
||||
|
||||
{
|
||||
void *lock;
|
||||
blender::ImBuf *ibuf = BKE_image_acquire_ibuf(b_image, &b_iuser, &lock);
|
||||
if (ibuf) {
|
||||
is_float = ibuf->float_data() != nullptr;
|
||||
is_data = ibuf->colorspace_is_data();
|
||||
metadata.width = ibuf->x;
|
||||
metadata.height = ibuf->y;
|
||||
metadata.channels = (is_float) ? ibuf->channels : 4;
|
||||
metadata.is_unassociated_alpha = !is_float;
|
||||
}
|
||||
else {
|
||||
metadata.width = 0;
|
||||
metadata.height = 0;
|
||||
metadata.channels = 0;
|
||||
}
|
||||
BKE_image_release_ibuf(b_image, ibuf, lock);
|
||||
}
|
||||
|
||||
if (is_float) {
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT;
|
||||
}
|
||||
else {
|
||||
metadata.channels = 4;
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT4;
|
||||
}
|
||||
|
||||
/* Float images are already converted on the Blender side,
|
||||
* no need to do anything in Cycles. */
|
||||
metadata.colorspace = (is_data) ? u_colorspace_data : u_colorspace_scene_linear;
|
||||
}
|
||||
else {
|
||||
/* In some cases (e.g. #94135), the colorspace setting in Blender gets updated as part of the
|
||||
* metadata queries in this function, so update the colorspace setting here. */
|
||||
metadata.colorspace = (is_data) ? u_colorspace_data :
|
||||
ustring(b_image->colorspace_settings.name);
|
||||
metadata.type = IMAGE_DATA_TYPE_BYTE4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void load_float_pixels(const blender::ImBuf *ibuf,
|
||||
const ImageMetaData &metadata,
|
||||
float *out_pixels)
|
||||
{
|
||||
const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
|
||||
const int out_channels = metadata.channels;
|
||||
const int in_channels = ibuf->channels;
|
||||
const float *in_pixels = ibuf->float_data();
|
||||
|
||||
if (in_pixels && out_channels == in_channels) {
|
||||
/* Straight copy pixel data. */
|
||||
memcpy(out_pixels, in_pixels, num_pixels * out_channels * sizeof(float));
|
||||
}
|
||||
else if (in_pixels && out_channels == 4) {
|
||||
/* Fill channels to 4. */
|
||||
float *out_pixel = out_pixels;
|
||||
const float *in_pixel = in_pixels;
|
||||
for (size_t i = 0; i < num_pixels; i++) {
|
||||
out_pixel[0] = in_pixel[0];
|
||||
out_pixel[1] = (in_channels >= 2) ? in_pixel[1] : 0.0f;
|
||||
out_pixel[2] = (in_channels >= 3) ? in_pixel[2] : 0.0f;
|
||||
out_pixel[3] = (in_channels >= 4) ? in_pixel[3] : 1.0f;
|
||||
out_pixel += out_channels;
|
||||
in_pixel += in_channels;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Missing or invalid pixel data. */
|
||||
if (out_channels == 1) {
|
||||
std::fill(out_pixels, out_pixels + num_pixels, 0.0f);
|
||||
}
|
||||
else {
|
||||
std::fill((float4 *)out_pixels,
|
||||
(float4 *)out_pixels + num_pixels,
|
||||
make_float4(1.0f, 0.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void load_half_pixels(const blender::ImBuf *ibuf,
|
||||
const ImageMetaData &metadata,
|
||||
half *out_pixels)
|
||||
{
|
||||
/* Half float. Blender does not have a half type, but in some cases
|
||||
* we up-sample byte to half to avoid precision loss for colorspace
|
||||
* conversion. */
|
||||
const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
|
||||
const int out_channels = metadata.channels;
|
||||
const uchar *in_pixels = ibuf->byte_data();
|
||||
|
||||
if (in_pixels) {
|
||||
/* Convert uchar to half. */
|
||||
const uchar *in_pixel = in_pixels;
|
||||
half *out_pixel = out_pixels;
|
||||
for (size_t i = 0; i < num_pixels; i++) {
|
||||
for (int c = 0; c < out_channels; c++, in_pixel++, out_pixel++) {
|
||||
*out_pixel = float_to_half_image(util_image_cast_to_float(*in_pixel));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Missing or invalid pixel data. */
|
||||
if (out_channels == 1) {
|
||||
std::fill(out_pixels, out_pixels + num_pixels, float_to_half_image(0.0f));
|
||||
}
|
||||
else {
|
||||
std::fill((half4 *)out_pixels,
|
||||
(half4 *)out_pixels + num_pixels,
|
||||
float4_to_half4_display(make_float4(1.0f, 0.0f, 1.0f, 1.0f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void load_byte_pixels(const blender::ImBuf *ibuf,
|
||||
const ImageMetaData &metadata,
|
||||
uchar *out_pixels)
|
||||
{
|
||||
const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
|
||||
const int out_channels = metadata.channels;
|
||||
const int in_channels = 4;
|
||||
const uchar *in_pixels = ibuf->byte_data();
|
||||
|
||||
if (in_pixels) {
|
||||
/* Straight copy pixel data. */
|
||||
memcpy(out_pixels, in_pixels, num_pixels * in_channels * sizeof(unsigned char));
|
||||
}
|
||||
else {
|
||||
/* Missing or invalid pixel data. */
|
||||
if (out_channels == 1) {
|
||||
std::fill(out_pixels, out_pixels + num_pixels, 0.0f);
|
||||
}
|
||||
else {
|
||||
std::fill(
|
||||
(uchar4 *)out_pixels, (uchar4 *)out_pixels + num_pixels, make_uchar4(255, 0, 255, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata, void *out_pixels)
|
||||
{
|
||||
void *lock;
|
||||
blender::ImBuf *ibuf = BKE_image_acquire_ibuf(b_image, &b_iuser, &lock);
|
||||
|
||||
/* Image changed since we requested metadata, assume we'll get a signal to reload it later. */
|
||||
const bool mismatch = (ibuf == nullptr || ibuf->x != metadata.width ||
|
||||
ibuf->y != metadata.height);
|
||||
|
||||
if (!mismatch) {
|
||||
if (metadata.type == IMAGE_DATA_TYPE_FLOAT || metadata.type == IMAGE_DATA_TYPE_FLOAT4) {
|
||||
load_float_pixels(ibuf, metadata, (float *)out_pixels);
|
||||
}
|
||||
else if (metadata.type == IMAGE_DATA_TYPE_HALF || metadata.type == IMAGE_DATA_TYPE_HALF4) {
|
||||
load_half_pixels(ibuf, metadata, (half *)out_pixels);
|
||||
}
|
||||
else {
|
||||
load_byte_pixels(ibuf, metadata, (uchar *)out_pixels);
|
||||
}
|
||||
}
|
||||
|
||||
BKE_image_release_ibuf(b_image, ibuf, lock);
|
||||
|
||||
/* Free image buffers to save memory during render. */
|
||||
if (free_cache) {
|
||||
BKE_image_free_buffers_ex(b_image, true);
|
||||
}
|
||||
|
||||
if (!mismatch) {
|
||||
metadata.conform_pixels(out_pixels);
|
||||
}
|
||||
|
||||
return !mismatch;
|
||||
}
|
||||
|
||||
string BlenderImageLoader::name() const
|
||||
{
|
||||
return b_image->id.name + 2;
|
||||
}
|
||||
|
||||
bool BlenderImageLoader::equals(const ImageLoader &other) const
|
||||
{
|
||||
const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other;
|
||||
return b_image == other_loader.b_image && b_iuser.framenr == other_loader.b_iuser.framenr &&
|
||||
b_iuser.tile == other_loader.b_iuser.tile &&
|
||||
cached_update_count == other_loader.cached_update_count;
|
||||
}
|
||||
|
||||
int BlenderImageLoader::get_tile_number() const
|
||||
{
|
||||
return b_iuser.tile;
|
||||
}
|
||||
|
||||
void BlenderSession::builtin_images_load()
|
||||
{
|
||||
/* Force builtin images to be loaded along with Blender data sync. This
|
||||
* is needed because we may be reading from depsgraph evaluated data which
|
||||
* can be freed by Blender before Cycles reads it.
|
||||
*
|
||||
* TODO: the assumption that no further access to builtin image data will
|
||||
* happen is really weak, and likely to break in the future. We should find
|
||||
* a better solution to hand over the data directly to the image manager
|
||||
* instead of through callbacks whose timing is difficult to control. */
|
||||
ImageManager *manager = session->scene->image_manager.get();
|
||||
Device *device = session->device.get();
|
||||
manager->device_load_builtin(device, session->scene.get(), session->progress);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
42
blender-5.2.0/intern/cycles/blender/image.h
Normal file
42
blender-5.2.0/intern/cycles/blender/image.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "scene/image_loader.h"
|
||||
#include "scene/image_vdb.h"
|
||||
|
||||
#include "util/types.h"
|
||||
|
||||
struct Image;
|
||||
struct ImageUser;
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BlenderImageLoader : public ImageLoader {
|
||||
public:
|
||||
BlenderImageLoader(blender::Image *b_image,
|
||||
blender::ImageUser *b_iuser,
|
||||
const int frame,
|
||||
const int tile_number,
|
||||
const bool is_preview_render);
|
||||
|
||||
bool load_metadata(ImageMetaData &metadata,
|
||||
const ImageLoaderParams ¶ms,
|
||||
Progress &progress) override;
|
||||
bool load_pixels(const ImageMetaData &metadata, void *pixels) override;
|
||||
string name() const override;
|
||||
bool equals(const ImageLoader &other) const override;
|
||||
|
||||
int get_tile_number() const override;
|
||||
|
||||
blender::Image *b_image;
|
||||
blender::ImageUser b_iuser;
|
||||
bool free_cache;
|
||||
uint64_t cached_update_count;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
26
blender-5.2.0/intern/cycles/blender/implicit_sharing.cpp
Normal file
26
blender-5.2.0/intern/cycles/blender/implicit_sharing.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "util/implicit_sharing.h"
|
||||
|
||||
#include "BLI_implicit_sharing.hh"
|
||||
|
||||
#include "blender/CCL_api.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void CCL_implicit_sharing_init()
|
||||
{
|
||||
ccl::implicit_sharing_init(
|
||||
[](ccl::ImplicitSharingInfo data) {
|
||||
const auto *info = static_cast<const blender::ImplicitSharingInfo *>(data);
|
||||
info->add_user();
|
||||
},
|
||||
[](ccl::ImplicitSharingInfo data) {
|
||||
const auto *info = static_cast<const blender::ImplicitSharingInfo *>(data);
|
||||
info->remove_user_and_delete_if_last();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
155
blender-5.2.0/intern/cycles/blender/light.cpp
Normal file
155
blender-5.2.0/intern/cycles/blender/light.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/light.h"
|
||||
|
||||
#include "DNA_light_types.h"
|
||||
|
||||
#include "DNA_world_types.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
#include "scene/object.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
void BlenderSync::sync_light(BObjectInfo &b_ob_info, Light *light)
|
||||
{
|
||||
blender::Light &b_light = *blender::id_cast<blender::Light *>(b_ob_info.object_data);
|
||||
|
||||
light->name = b_light.id.name + 2;
|
||||
|
||||
if (PointLight *point_light = dynamic_cast<PointLight *>(light)) {
|
||||
point_light->set_radius(b_light.radius);
|
||||
point_light->set_is_sphere(!(b_light.mode & blender::LA_USE_SOFT_FALLOFF));
|
||||
}
|
||||
else if (AreaLight *area_light = dynamic_cast<AreaLight *>(light)) {
|
||||
area_light->set_sizeu(b_light.area_size);
|
||||
area_light->set_spread(b_light.area_spread);
|
||||
if (b_light.area_shape == blender::LA_AREA_SQUARE ||
|
||||
b_light.area_shape == blender::LA_AREA_DISK)
|
||||
{
|
||||
area_light->set_sizev(area_light->get_sizeu());
|
||||
}
|
||||
else {
|
||||
area_light->set_sizev(b_light.area_sizey);
|
||||
}
|
||||
area_light->set_ellipse(b_light.area_shape == blender::LA_AREA_DISK ||
|
||||
b_light.area_shape == blender::LA_AREA_ELLIPSE);
|
||||
}
|
||||
else if (SunLight *sun_light = dynamic_cast<SunLight *>(light)) {
|
||||
sun_light->set_angle(b_light.sun_angle);
|
||||
}
|
||||
if (SpotLight *spot_light = dynamic_cast<SpotLight *>(light)) {
|
||||
spot_light->set_angle(b_light.spotsize);
|
||||
spot_light->set_smooth(b_light.spotblend);
|
||||
}
|
||||
|
||||
blender::PointerRNA light_rna_ptr = RNA_id_pointer_create(&b_light.id);
|
||||
|
||||
/* Color and strength. */
|
||||
float3 light_color = make_float3(b_light.r, b_light.g, b_light.b);
|
||||
if (b_light.mode & blender::LA_USE_TEMPERATURE) {
|
||||
float color[3];
|
||||
RNA_float_get_array(&light_rna_ptr, "temperature_color", color);
|
||||
light_color *= make_float3(color[0], color[1], color[2]);
|
||||
}
|
||||
|
||||
const float3 strength = light_color * b_light.energy * exp2f(b_light.exposure);
|
||||
light->set_strength(strength);
|
||||
|
||||
/* normalize */
|
||||
light->set_normalize(!(b_light.mode & blender::LA_UNNORMALIZED));
|
||||
|
||||
/* shadow */
|
||||
blender::PointerRNA clight = RNA_pointer_get(&light_rna_ptr, "cycles");
|
||||
light->set_cast_shadow(b_light.mode & blender::LA_SHADOW);
|
||||
light->set_use_mis(get_boolean(clight, "use_multiple_importance_sampling"));
|
||||
|
||||
/* caustics light */
|
||||
light->set_use_caustics(get_boolean(clight, "is_caustics_light"));
|
||||
|
||||
light->set_max_bounces(get_int(clight, "max_bounces"));
|
||||
|
||||
if (AreaLight *area_light = dynamic_cast<AreaLight *>(light)) {
|
||||
area_light->set_is_portal(get_boolean(clight, "is_portal"));
|
||||
}
|
||||
|
||||
/* tag */
|
||||
light->tag_update(scene);
|
||||
}
|
||||
|
||||
void BlenderSync::sync_background_light(blender::bScreen *b_screen, blender::View3D *b_v3d)
|
||||
{
|
||||
blender::World *b_world = view_layer.world_override ? view_layer.world_override : b_scene->world;
|
||||
|
||||
if (b_world) {
|
||||
blender::PointerRNA world_rna_ptr = RNA_id_pointer_create(&b_world->id);
|
||||
blender::PointerRNA cworld = RNA_pointer_get(&world_rna_ptr, "cycles");
|
||||
|
||||
enum SamplingMethod { SAMPLING_NONE = 0, SAMPLING_AUTOMATIC, SAMPLING_MANUAL, SAMPLING_NUM };
|
||||
const int sampling_method = get_enum(
|
||||
cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
|
||||
const bool sample_as_light = (sampling_method != SAMPLING_NONE);
|
||||
|
||||
/* Create object. */
|
||||
Object *object;
|
||||
const ObjectKey object_key(b_world, nullptr, b_world, false);
|
||||
bool update = object_map.add_or_update(&object, &b_world->id, &b_world->id, object_key);
|
||||
if (update) {
|
||||
/* Lights should be shadow catchers by default. */
|
||||
object->set_is_shadow_catcher(true);
|
||||
}
|
||||
|
||||
object->set_lightgroup(
|
||||
ustring((b_world && b_world->lightgroup) ? b_world->lightgroup->name : ""));
|
||||
object->set_asset_name(ustring(b_world->id.name + 2));
|
||||
|
||||
/* Create geometry. */
|
||||
const GeometryKey geom_key{b_world, Geometry::BACKGROUND_LIGHT};
|
||||
Geometry *geom = geometry_map.find(geom_key);
|
||||
if (geom) {
|
||||
update |= geometry_map.update(geom, &b_world->id);
|
||||
}
|
||||
else {
|
||||
geom = scene->create_light_node<BackgroundLight>();
|
||||
geometry_map.add(geom_key, geom);
|
||||
object->set_geometry(geom);
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (update || world_recalc || b_world != world_map) {
|
||||
/* Initialize light geometry. */
|
||||
BackgroundLight *light = static_cast<BackgroundLight *>(geom);
|
||||
|
||||
array<Node *> used_shaders;
|
||||
used_shaders.push_back_slow(scene->default_background);
|
||||
light->set_used_shaders(used_shaders);
|
||||
|
||||
if (sampling_method == SAMPLING_MANUAL) {
|
||||
light->set_map_resolution(get_int(cworld, "sample_map_resolution"));
|
||||
}
|
||||
else {
|
||||
light->set_map_resolution(0);
|
||||
}
|
||||
|
||||
light->set_use_mis(sample_as_light);
|
||||
light->set_max_bounces(get_int(cworld, "max_bounces"));
|
||||
|
||||
/* Caustic light. */
|
||||
light->set_use_caustics(get_boolean(cworld, "is_caustics_light"));
|
||||
|
||||
light->set_cast_shadow(get_boolean(cworld, "use_shadows"));
|
||||
|
||||
light->tag_update(scene);
|
||||
|
||||
geometry_map.set_recalc(b_world);
|
||||
}
|
||||
}
|
||||
|
||||
world_map = b_world;
|
||||
world_recalc = false;
|
||||
viewport_parameters = BlenderViewportParameters(b_screen, b_v3d, use_developer_ui);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
60
blender-5.2.0/intern/cycles/blender/light_linking.cpp
Normal file
60
blender-5.2.0/intern/cycles/blender/light_linking.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "blender/light_linking.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static const blender::LightLinking *get_light_linking(const blender::Object &b_object)
|
||||
{
|
||||
return b_object.light_linking;
|
||||
}
|
||||
|
||||
uint64_t BlenderLightLink::get_light_set_membership(const blender::Object * /*parent*/,
|
||||
const blender::Object &object)
|
||||
{
|
||||
const blender::LightLinking *light_linking = get_light_linking(object);
|
||||
return (light_linking) ? light_linking->runtime.light_set_membership : LIGHT_LINK_MASK_ALL;
|
||||
}
|
||||
|
||||
uint BlenderLightLink::get_receiver_light_set(const blender::Object *parent,
|
||||
const blender::Object &object)
|
||||
{
|
||||
if (parent) {
|
||||
const blender::LightLinking *parent_light_linking = get_light_linking(*parent);
|
||||
if (parent_light_linking && parent_light_linking->runtime.receiver_light_set) {
|
||||
return parent_light_linking->runtime.receiver_light_set;
|
||||
}
|
||||
}
|
||||
|
||||
const blender::LightLinking *light_linking = get_light_linking(object);
|
||||
return (light_linking) ? light_linking->runtime.receiver_light_set : 0;
|
||||
}
|
||||
|
||||
uint64_t BlenderLightLink::get_shadow_set_membership(const blender::Object * /*parent*/,
|
||||
const blender::Object &object)
|
||||
{
|
||||
const blender::LightLinking *light_linking = get_light_linking(object);
|
||||
return (light_linking) ? light_linking->runtime.shadow_set_membership : LIGHT_LINK_MASK_ALL;
|
||||
}
|
||||
|
||||
uint BlenderLightLink::get_blocker_shadow_set(const blender::Object *parent,
|
||||
const blender::Object &object)
|
||||
{
|
||||
if (parent) {
|
||||
const blender::LightLinking *parent_light_linking = get_light_linking(*parent);
|
||||
if (parent_light_linking && parent_light_linking->runtime.blocker_shadow_set) {
|
||||
return parent_light_linking->runtime.blocker_shadow_set;
|
||||
}
|
||||
}
|
||||
|
||||
const blender::LightLinking *light_linking = get_light_linking(object);
|
||||
return (light_linking) ? light_linking->runtime.blocker_shadow_set : 0;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/blender/light_linking.h
Normal file
28
blender-5.2.0/intern/cycles/blender/light_linking.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/types_base.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
struct Object;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BlenderLightLink {
|
||||
public:
|
||||
static uint64_t get_light_set_membership(const blender::Object *parent,
|
||||
const blender::Object &object);
|
||||
static uint get_receiver_light_set(const blender::Object *parent, const blender::Object &object);
|
||||
|
||||
static uint64_t get_shadow_set_membership(const blender::Object *parent,
|
||||
const blender::Object &object);
|
||||
static uint get_blocker_shadow_set(const blender::Object *parent, const blender::Object &object);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
71
blender-5.2.0/intern/cycles/blender/logging.cpp
Normal file
71
blender-5.2.0/intern/cycles/blender/logging.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "blender/CCL_api.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"cycles"};
|
||||
|
||||
void CCL_log_init()
|
||||
{
|
||||
/* Set callback to pass log messages to CLOG. */
|
||||
ccl::log_init(
|
||||
[](const ccl::LogLevel level, const char *file_line, const char *func, const char *msg) {
|
||||
const CLG_LogType *log_type = CLOG_ENSURE(&LOG);
|
||||
switch (level) {
|
||||
case ccl::LOG_LEVEL_FATAL:
|
||||
case ccl::LOG_LEVEL_DFATAL:
|
||||
CLG_log_str(log_type, CLG_LEVEL_FATAL, file_line, func, msg);
|
||||
return;
|
||||
case ccl::LOG_LEVEL_ERROR:
|
||||
case ccl::LOG_LEVEL_DERROR:
|
||||
CLG_log_str(log_type, CLG_LEVEL_ERROR, file_line, func, msg);
|
||||
return;
|
||||
case ccl::LOG_LEVEL_WARNING:
|
||||
case ccl::LOG_LEVEL_DWARNING:
|
||||
CLG_log_str(log_type, CLG_LEVEL_WARN, file_line, func, msg);
|
||||
return;
|
||||
case ccl::LOG_LEVEL_INFO:
|
||||
case ccl::LOG_LEVEL_INFO_IMPORTANT:
|
||||
CLG_log_str(log_type, CLG_LEVEL_INFO, file_line, func, msg);
|
||||
return;
|
||||
case ccl::LOG_LEVEL_DEBUG:
|
||||
CLG_log_str(log_type, CLG_LEVEL_DEBUG, file_line, func, msg);
|
||||
return;
|
||||
case ccl::LOG_LEVEL_TRACE:
|
||||
case ccl::LOG_LEVEL_UNKNOWN:
|
||||
CLG_log_str(log_type, CLG_LEVEL_TRACE, file_line, func, msg);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
/* Map log level from CLOG. */
|
||||
const CLG_LogType *log_type = CLOG_ENSURE(&LOG);
|
||||
switch (log_type->level) {
|
||||
case CLG_LEVEL_FATAL:
|
||||
ccl::log_level_set(ccl::LOG_LEVEL_FATAL);
|
||||
break;
|
||||
case CLG_LEVEL_ERROR:
|
||||
ccl::log_level_set(ccl::LOG_LEVEL_ERROR);
|
||||
break;
|
||||
case CLG_LEVEL_WARN:
|
||||
ccl::log_level_set(ccl::LOG_LEVEL_WARNING);
|
||||
break;
|
||||
case CLG_LEVEL_INFO:
|
||||
ccl::log_level_set(ccl::LOG_LEVEL_INFO);
|
||||
break;
|
||||
case CLG_LEVEL_DEBUG:
|
||||
ccl::log_level_set(ccl::LOG_LEVEL_DEBUG);
|
||||
break;
|
||||
case CLG_LEVEL_TRACE:
|
||||
ccl::log_level_set(ccl::LOG_LEVEL_TRACE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1153
blender-5.2.0/intern/cycles/blender/mesh.cpp
Normal file
1153
blender-5.2.0/intern/cycles/blender/mesh.cpp
Normal file
File diff suppressed because it is too large
Load Diff
700
blender-5.2.0/intern/cycles/blender/object.cpp
Normal file
700
blender-5.2.0/intern/cycles/blender/object.cpp
Normal file
@@ -0,0 +1,700 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "blender/light_linking.h"
|
||||
#include "blender/object_cull.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "scene/camera.h"
|
||||
#include "scene/integrator.h"
|
||||
#include "scene/light.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/particles.h"
|
||||
#include "scene/scene.h"
|
||||
#include "scene/shader.h"
|
||||
#include "scene/shader_graph.h"
|
||||
#include "scene/shader_nodes.h"
|
||||
|
||||
#include "util/hash.h"
|
||||
#include "util/log.h"
|
||||
#include "util/task.h"
|
||||
|
||||
#include "BKE_duplilist.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
using blender::Object;
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Utilities */
|
||||
|
||||
bool BlenderSync::BKE_object_is_modified(blender::Object &b_ob)
|
||||
{
|
||||
/* test if we can instance or if the object is modified */
|
||||
if (b_ob.type == blender::OB_MBALL) {
|
||||
/* Multi-user and dupli meta-balls are fused, can't instance. */
|
||||
return true;
|
||||
}
|
||||
const int settings = preview ? blender::eModifierMode_Realtime : blender::eModifierMode_Render;
|
||||
if ((blender::BKE_object_is_modified(b_scene, &b_ob) & settings) != 0) {
|
||||
/* modifiers */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Object level material links. Note the geometry material slot array may not match
|
||||
* the object matbits array, so we need to guard against out of bounds. */
|
||||
for (const int i : blender::IndexRange(BKE_object_material_count_eval(&b_ob))) {
|
||||
if (i < b_ob.totcol && b_ob.matbits && b_ob.matbits[i] != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info)
|
||||
{
|
||||
blender::ID *b_ob_data = b_ob_info.object_data;
|
||||
|
||||
if (!b_ob_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const blender::ObjectType type = b_ob_info.iter_object->type;
|
||||
|
||||
if (type == blender::OB_VOLUME || type == blender::OB_CURVES || type == blender::OB_POINTCLOUD ||
|
||||
type == blender::OB_LAMP)
|
||||
{
|
||||
/* Will be exported as geometry. */
|
||||
return true;
|
||||
}
|
||||
|
||||
return GS(b_ob_data->name) == blender::ID_ME;
|
||||
}
|
||||
|
||||
bool BlenderSync::object_can_have_geometry(blender::Object &b_ob)
|
||||
{
|
||||
const blender::ObjectType type = b_ob.type;
|
||||
switch (type) {
|
||||
case blender::OB_MESH:
|
||||
case blender::OB_CURVES_LEGACY:
|
||||
case blender::OB_SURF:
|
||||
case blender::OB_MBALL:
|
||||
case blender::OB_FONT:
|
||||
case blender::OB_CURVES:
|
||||
case blender::OB_POINTCLOUD:
|
||||
case blender::OB_VOLUME:
|
||||
/* TODO(weizhen): OB_LAMP */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderSync::object_is_light(blender::Object &b_ob)
|
||||
{
|
||||
blender::ID *b_ob_data = object_get_data(b_ob, true);
|
||||
|
||||
return (b_ob_data && GS(b_ob_data->name) == blender::ID_LA);
|
||||
}
|
||||
|
||||
bool BlenderSync::object_is_camera(blender::Object &b_ob)
|
||||
{
|
||||
blender::ID *b_ob_data = object_get_data(b_ob, true);
|
||||
|
||||
return (b_ob_data && GS(b_ob_data->name) == blender::ID_CA);
|
||||
}
|
||||
|
||||
void BlenderSync::sync_object_motion_init(blender::Object &b_parent,
|
||||
blender::Object &b_ob,
|
||||
Object *object)
|
||||
{
|
||||
/* Initialize motion blur for object, detecting if it's enabled and creating motion
|
||||
* steps array if so. */
|
||||
array<Transform> motion = object->get_motion();
|
||||
|
||||
Geometry *geom = object->get_geometry();
|
||||
if (!geom) {
|
||||
return;
|
||||
}
|
||||
|
||||
int motion_steps = 0;
|
||||
bool use_motion_blur = false;
|
||||
|
||||
const Scene::MotionType need_motion = scene->need_motion();
|
||||
if (need_motion == Scene::MOTION_BLUR) {
|
||||
motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS);
|
||||
if (motion_steps && object_use_deform_motion(b_parent, b_ob)) {
|
||||
use_motion_blur = true;
|
||||
}
|
||||
}
|
||||
else if (need_motion != Scene::MOTION_NONE) {
|
||||
motion_steps = 3;
|
||||
}
|
||||
|
||||
geom->set_use_motion_blur(use_motion_blur);
|
||||
|
||||
motion.resize(motion_steps, transform_empty());
|
||||
|
||||
if (motion_steps) {
|
||||
motion[motion_steps / 2] = object->get_tfm();
|
||||
|
||||
/* update motion socket before trying to access object->motion_time */
|
||||
object->set_motion(motion);
|
||||
|
||||
for (size_t step = 0; step < motion_steps; step++) {
|
||||
motion_times.insert(object->motion_time(step));
|
||||
}
|
||||
}
|
||||
else {
|
||||
object->set_motion(motion);
|
||||
}
|
||||
}
|
||||
|
||||
Object *BlenderSync::sync_object(blender::ViewLayer &b_view_layer,
|
||||
blender::Object &b_ob,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
const float motion_time,
|
||||
bool use_particle_hair,
|
||||
bool show_lights,
|
||||
BlenderObjectCulling &culling,
|
||||
TaskPool *geom_task_pool)
|
||||
{
|
||||
const bool is_instance = b_deg_iter_data.dupli_object_current;
|
||||
blender::Object *b_parent = is_instance ? b_deg_iter_data.dupli_parent : &b_ob;
|
||||
blender::Object *b_real_object = is_instance ? b_deg_iter_data.dupli_object_current->ob : &b_ob;
|
||||
const bool use_adaptive_subdiv = object_subdivision_type(
|
||||
*b_real_object, preview, use_adaptive_subdivision) !=
|
||||
Mesh::SUBDIVISION_NONE;
|
||||
BObjectInfo b_ob_info{
|
||||
&b_ob, b_real_object, object_get_data(b_ob, use_adaptive_subdiv), use_adaptive_subdiv};
|
||||
const bool motion = motion_time != 0.0f;
|
||||
const Transform tfm = get_transform(b_ob.object_to_world());
|
||||
const int *persistent_id = nullptr;
|
||||
if (is_instance) {
|
||||
persistent_id = b_deg_iter_data.dupli_object_current->persistent_id;
|
||||
if (!motion && !b_ob_info.is_real_object_data()) {
|
||||
/* Remember which object data the geometry is coming from, so that we can sync it when the
|
||||
* object has changed. */
|
||||
instance_geometries_by_object[b_ob_info.real_object].insert(b_ob_info.object_data);
|
||||
}
|
||||
}
|
||||
|
||||
/* only interested in object that we can create geometry from */
|
||||
if (!object_is_geometry(b_ob_info)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Perform object culling. */
|
||||
if (object_is_light(b_ob)) {
|
||||
if (!show_lights) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if (culling.test(scene, b_ob, tfm)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Visibility flags for both parent and child. */
|
||||
blender::PointerRNA b_ob_rna_ptr = RNA_id_pointer_create(&b_ob.id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&b_ob_rna_ptr, "cycles");
|
||||
/* Note base_parent is null for objects from the background scene. */
|
||||
const blender::Base *base_parent = BKE_view_layer_base_find(&b_view_layer, b_parent);
|
||||
const bool use_holdout = (base_parent && (base_parent->flag & blender::BASE_HOLDOUT) != 0) ||
|
||||
((b_parent->visibility_flag & blender::OB_HOLDOUT) != 0);
|
||||
PathRayVisibility visibility = object_ray_visibility(b_ob);
|
||||
|
||||
if (b_parent != &b_ob) {
|
||||
visibility &= object_ray_visibility(*b_parent);
|
||||
}
|
||||
|
||||
/* TODO: make holdout objects on excluded layer invisible for non-camera rays. */
|
||||
#if 0
|
||||
if (use_holdout && (layer_flag & view_layer.exclude_layer)) {
|
||||
visibility &= ~(PATH_RAY_VISIBILITY_ALL & ~PATH_RAY_VISIBILITY_CAMERA);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clear camera visibility for indirect only objects. */
|
||||
const bool use_indirect_only = !use_holdout && base_parent &&
|
||||
((base_parent->flag & blender::BASE_INDIRECT_ONLY) != 0);
|
||||
if (use_indirect_only) {
|
||||
visibility &= ~PATH_RAY_VISIBILITY_CAMERA;
|
||||
}
|
||||
|
||||
/* Don't export completely invisible objects. */
|
||||
if (visibility == PATH_RAY_VISIBILITY_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Use task pool only for non-instances, since sync_dupli_particle accesses
|
||||
* geometry. This restriction should be removed for better performance. */
|
||||
TaskPool *object_geom_task_pool = (is_instance) ? nullptr : geom_task_pool;
|
||||
|
||||
/* key to lookup object */
|
||||
const ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, use_particle_hair);
|
||||
Object *object;
|
||||
|
||||
/* motion vector case */
|
||||
if (motion) {
|
||||
object = object_map.find(key);
|
||||
|
||||
if (object && object->use_motion()) {
|
||||
/* Set transform at matching motion time step. */
|
||||
const int time_index = object->motion_step(motion_time);
|
||||
if (time_index >= 0) {
|
||||
object->set_motion_tfm(tfm, time_index);
|
||||
}
|
||||
|
||||
/* mesh deformation */
|
||||
if (object->get_geometry()) {
|
||||
sync_geometry_motion(
|
||||
b_ob_info, object, motion_time, use_particle_hair, object_geom_task_pool);
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/* test if we need to sync */
|
||||
bool object_updated = object_map.add_or_update(&object, &b_ob.id, &b_parent->id, key) ||
|
||||
!object->tfm_equals(tfm);
|
||||
|
||||
/* mesh sync */
|
||||
Geometry *geometry = sync_geometry(
|
||||
b_ob_info, object_updated, use_particle_hair, object_geom_task_pool);
|
||||
object->set_geometry(geometry);
|
||||
|
||||
/* special case not tracked by object update flags */
|
||||
|
||||
if (sync_object_attributes(b_ob, b_deg_iter_data, object)) {
|
||||
object_updated = true;
|
||||
}
|
||||
|
||||
/* holdout */
|
||||
object->set_use_holdout(use_holdout);
|
||||
|
||||
object->set_visibility(visibility);
|
||||
|
||||
object->set_is_shadow_catcher((b_ob.visibility_flag & blender::OB_SHADOW_CATCHER) != 0 ||
|
||||
(b_parent->visibility_flag & blender::OB_SHADOW_CATCHER) != 0);
|
||||
|
||||
object->set_shadow_terminator_shading_offset(b_ob.shadow_terminator_shading_offset);
|
||||
|
||||
object->set_shadow_terminator_geometry_offset(b_ob.shadow_terminator_geometry_offset);
|
||||
|
||||
float ao_distance = get_float(cobject, "ao_distance");
|
||||
if (ao_distance == 0.0f && b_parent != &b_ob) {
|
||||
blender::PointerRNA b_parent_rna_ptr = RNA_id_pointer_create(&b_parent->id);
|
||||
blender::PointerRNA cparent = RNA_pointer_get(&b_parent_rna_ptr, "cycles");
|
||||
ao_distance = get_float(cparent, "ao_distance");
|
||||
}
|
||||
object->set_ao_distance(ao_distance);
|
||||
|
||||
const bool is_caustics_caster = get_boolean(cobject, "is_caustics_caster");
|
||||
object->set_is_caustics_caster(is_caustics_caster);
|
||||
|
||||
const bool is_caustics_receiver = get_boolean(cobject, "is_caustics_receiver");
|
||||
object->set_is_caustics_receiver(is_caustics_receiver);
|
||||
|
||||
object->set_is_bake_target(b_ob_info.real_object == b_bake_target);
|
||||
|
||||
/* sync the asset name for Cryptomatte */
|
||||
blender::Object *parent = b_ob.parent;
|
||||
ustring parent_name;
|
||||
if (parent) {
|
||||
while (parent->parent) {
|
||||
parent = parent->parent;
|
||||
}
|
||||
parent_name = BKE_id_name(parent->id);
|
||||
}
|
||||
else {
|
||||
parent_name = BKE_id_name(b_ob.id);
|
||||
}
|
||||
object->set_asset_name(parent_name);
|
||||
|
||||
/* object sync
|
||||
* transform comparison should not be needed, but duplis don't work perfect
|
||||
* in the depsgraph and may not signal changes, so this is a workaround */
|
||||
const bool do_sync = object->is_modified() || object_updated ||
|
||||
(object->get_geometry() && object->get_geometry()->is_modified());
|
||||
if (do_sync) {
|
||||
object->name = BKE_id_name(b_ob.id);
|
||||
object->set_pass_id(b_ob.index);
|
||||
const float *object_color = b_ob.color;
|
||||
object->set_color(make_float3(object_color[0], object_color[1], object_color[2]));
|
||||
object->set_alpha(object_color[3]);
|
||||
object->set_tfm(tfm);
|
||||
|
||||
/* dupli texture coordinates and random_id */
|
||||
if (is_instance) {
|
||||
const float *orco = b_deg_iter_data.dupli_object_current->orco;
|
||||
object->set_dupli_generated(0.5f * make_float3(orco[0], orco[1], orco[2]) -
|
||||
make_float3(0.5f, 0.5f, 0.5f));
|
||||
const float *uv = b_deg_iter_data.dupli_object_current->uv;
|
||||
object->set_dupli_uv(make_float2(uv[0], uv[1]));
|
||||
object->set_random_id(b_deg_iter_data.dupli_object_current->random_id);
|
||||
}
|
||||
else {
|
||||
object->set_dupli_generated(zero_float3());
|
||||
object->set_dupli_uv(zero_float2());
|
||||
object->set_random_id(hash_uint2(hash_string(object->name.c_str()), 0));
|
||||
}
|
||||
|
||||
/* Light group and linking. */
|
||||
string lightgroup = b_ob.lightgroup ? b_ob.lightgroup->name : "";
|
||||
if (lightgroup.empty()) {
|
||||
lightgroup = b_parent->lightgroup ? b_parent->lightgroup->name : "";
|
||||
}
|
||||
object->set_lightgroup(ustring(lightgroup));
|
||||
|
||||
object->set_light_set_membership(BlenderLightLink::get_light_set_membership(b_parent, b_ob));
|
||||
object->set_receiver_light_set(BlenderLightLink::get_receiver_light_set(b_parent, b_ob));
|
||||
object->set_shadow_set_membership(BlenderLightLink::get_shadow_set_membership(b_parent, b_ob));
|
||||
object->set_blocker_shadow_set(BlenderLightLink::get_blocker_shadow_set(b_parent, b_ob));
|
||||
}
|
||||
|
||||
sync_object_motion_init(*b_parent, b_ob, object);
|
||||
|
||||
if (do_sync || object->motion_is_modified()) {
|
||||
object->tag_update(scene);
|
||||
}
|
||||
|
||||
if (is_instance) {
|
||||
/* Sync possible particle data. */
|
||||
sync_dupli_particle(*b_parent, b_deg_iter_data, b_ob, object);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static float4 lookup_instance_property(blender::Object &ob,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
const string &name,
|
||||
bool use_instancer)
|
||||
{
|
||||
blender::DupliObject *dupli = nullptr;
|
||||
blender::Object *dupli_parent = nullptr;
|
||||
|
||||
/* If requesting instance data, check the parent particle system and object. */
|
||||
if (use_instancer && b_deg_iter_data.dupli_object_current) {
|
||||
dupli = b_deg_iter_data.dupli_object_current;
|
||||
dupli_parent = b_deg_iter_data.dupli_parent;
|
||||
}
|
||||
|
||||
float4 value;
|
||||
BKE_object_dupli_find_rgba_attribute(&ob, dupli, dupli_parent, name.c_str(), &value.x);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool BlenderSync::sync_object_attributes(blender::Object &b_ob,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
Object *object)
|
||||
{
|
||||
/* Find which attributes are needed. */
|
||||
AttributeRequestSet requests = object->get_geometry()->needed_attributes();
|
||||
|
||||
/* Delete attributes that became unnecessary. */
|
||||
vector<ParamValue> &attributes = object->attributes;
|
||||
bool changed = false;
|
||||
|
||||
for (int i = attributes.size() - 1; i >= 0; i--) {
|
||||
if (!requests.find(attributes[i].name())) {
|
||||
attributes.erase(attributes.begin() + i);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update attribute values. */
|
||||
for (const AttributeRequest &req : requests.requests) {
|
||||
const ustring name = req.name;
|
||||
|
||||
std::string real_name;
|
||||
const int type = blender_attribute_name_split_type(name, &real_name);
|
||||
|
||||
if (type == blender::SHD_ATTRIBUTE_OBJECT || type == blender::SHD_ATTRIBUTE_INSTANCER) {
|
||||
const bool use_instancer = (type == blender::SHD_ATTRIBUTE_INSTANCER);
|
||||
float4 value = lookup_instance_property(b_ob, b_deg_iter_data, real_name, use_instancer);
|
||||
|
||||
/* Try finding the existing attribute value. */
|
||||
ParamValue *param = nullptr;
|
||||
|
||||
for (size_t i = 0; i < attributes.size(); i++) {
|
||||
if (attributes[i].name() == name) {
|
||||
param = &attributes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace or add the value. */
|
||||
const ParamValue new_param(name, TypeFloat4, 1, &value);
|
||||
assert(new_param.datasize() == sizeof(value));
|
||||
|
||||
if (!param) {
|
||||
changed = true;
|
||||
attributes.push_back(new_param);
|
||||
}
|
||||
else {
|
||||
/* Cannot use param->get<float4>, ParamValue storage is not guaranteed to be aligned. */
|
||||
const float *param_data = static_cast<const float *>(param->data());
|
||||
if (make_float4(param_data[0], param_data[1], param_data[2], param_data[3]) != value) {
|
||||
changed = true;
|
||||
*param = new_param;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Object Loop */
|
||||
|
||||
void BlenderSync::sync_objects(blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
const float motion_time)
|
||||
{
|
||||
/* Task pool for multithreaded geometry sync. */
|
||||
TaskPool geom_task_pool;
|
||||
|
||||
/* layer data */
|
||||
const bool motion = motion_time != 0.0f;
|
||||
|
||||
if (!motion) {
|
||||
/* prepare for sync */
|
||||
geometry_map.pre_sync();
|
||||
object_map.pre_sync();
|
||||
procedural_map.pre_sync();
|
||||
particle_system_map.pre_sync();
|
||||
motion_times.clear();
|
||||
}
|
||||
else {
|
||||
geometry_motion_synced.clear();
|
||||
}
|
||||
|
||||
if (!motion) {
|
||||
/* Object to geometry instance mapping is built for the reference time, as other
|
||||
* times just look up the corresponding geometry. */
|
||||
instance_geometries_by_object.clear();
|
||||
}
|
||||
|
||||
/* initialize culling */
|
||||
BlenderObjectCulling culling(scene, *b_scene);
|
||||
|
||||
/* object loop */
|
||||
bool cancel = false;
|
||||
const bool show_lights =
|
||||
BlenderViewportParameters(b_screen, b_v3d, use_developer_ui).use_scene_lights;
|
||||
|
||||
blender::ViewLayer &b_view_layer = *DEG_get_evaluated_view_layer(&b_depsgraph);
|
||||
|
||||
BKE_view_layer_synced_ensure(*b_data, b_scene, &b_view_layer);
|
||||
|
||||
blender::DEGObjectIterSettings deg_iter_settings{};
|
||||
deg_iter_settings.depsgraph = &b_depsgraph;
|
||||
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
||||
blender::DEGObjectIterData deg_iter_data{};
|
||||
deg_iter_data.settings = °_iter_settings;
|
||||
deg_iter_data.graph = deg_iter_settings.depsgraph;
|
||||
deg_iter_data.flag = deg_iter_settings.flags;
|
||||
|
||||
ITER_BEGIN (blender::DEG_iterator_objects_begin,
|
||||
blender::DEG_iterator_objects_next,
|
||||
blender::DEG_iterator_objects_end,
|
||||
°_iter_data,
|
||||
blender::Object *,
|
||||
b_ob)
|
||||
{
|
||||
/* Viewport visibility. */
|
||||
const bool show_in_viewport = !b_v3d || BKE_object_is_visible_in_viewport(b_v3d, b_ob);
|
||||
if (show_in_viewport == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Load per-object culling data. */
|
||||
culling.init_object(scene, *b_ob);
|
||||
|
||||
const int ob_visibility = BKE_object_visibility(b_ob, deg_iter_data.eval_mode);
|
||||
|
||||
/* Ensure the object geom supporting the hair is processed before adding
|
||||
* the hair processing task to the task pool, calling .to_mesh() on the
|
||||
* same object in parallel does not work. */
|
||||
const bool sync_hair = (ob_visibility & blender::OB_VISIBLE_PARTICLES) != 0 &&
|
||||
object_has_particle_hair(b_ob);
|
||||
|
||||
/* Object itself. */
|
||||
if ((ob_visibility & blender::OB_VISIBLE_SELF) != 0) {
|
||||
sync_object(b_view_layer,
|
||||
*b_ob,
|
||||
deg_iter_data,
|
||||
motion_time,
|
||||
false,
|
||||
show_lights,
|
||||
culling,
|
||||
sync_hair ? nullptr : &geom_task_pool);
|
||||
}
|
||||
|
||||
/* Particle hair as separate object. */
|
||||
if (sync_hair) {
|
||||
sync_object(b_view_layer,
|
||||
*b_ob,
|
||||
deg_iter_data,
|
||||
motion_time,
|
||||
true,
|
||||
show_lights,
|
||||
culling,
|
||||
&geom_task_pool);
|
||||
}
|
||||
|
||||
cancel = progress.get_cancel();
|
||||
if (cancel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ITER_END;
|
||||
|
||||
geom_task_pool.wait_work();
|
||||
|
||||
progress.set_sync_status("");
|
||||
|
||||
if (!cancel && !motion) {
|
||||
/* After object for world_use_portal. */
|
||||
sync_background_light(b_screen, b_v3d);
|
||||
|
||||
/* Handle removed data and modified pointers, as this may free memory, delete Nodes in the
|
||||
* right order to ensure that dependent data is freed after their users. Objects should be
|
||||
* freed before particle systems and geometries. */
|
||||
object_map.post_sync();
|
||||
geometry_map.post_sync();
|
||||
particle_system_map.post_sync();
|
||||
procedural_map.post_sync();
|
||||
}
|
||||
|
||||
if (motion) {
|
||||
geometry_motion_synced.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void BlenderSync::sync_objects_and_motion(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state)
|
||||
{
|
||||
/* get camera object here to deal with camera switch */
|
||||
blender::Object *b_cam = get_camera_object(b_v3d, b_rv3d);
|
||||
|
||||
const int frame_center = b_scene->r.cfra;
|
||||
const float subframe_center = b_scene->r.subframe;
|
||||
float frame_center_delta = 0.0f;
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_BLUR &&
|
||||
scene->camera->get_motion_position() != MOTION_POSITION_CENTER)
|
||||
{
|
||||
const float shuttertime = scene->camera->get_shuttertime();
|
||||
if (scene->camera->get_motion_position() == MOTION_POSITION_END) {
|
||||
frame_center_delta = -shuttertime * 0.5f;
|
||||
}
|
||||
else {
|
||||
assert(scene->camera->get_motion_position() == MOTION_POSITION_START);
|
||||
frame_center_delta = shuttertime * 0.5f;
|
||||
}
|
||||
|
||||
const float time = frame_center + subframe_center + frame_center_delta;
|
||||
const int frame = (int)floorf(time);
|
||||
const float subframe = time - frame;
|
||||
python_thread_state_restore(python_thread_state);
|
||||
RE_engine_frame_set(b_engine, frame, subframe);
|
||||
python_thread_state_save(python_thread_state);
|
||||
if (b_cam) {
|
||||
sync_camera_motion(b_render, b_cam, width, height, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
sync_objects(b_depsgraph, b_screen, b_v3d);
|
||||
|
||||
/* In the viewport, only motion between previous frame and current frame is of interest, which is
|
||||
* kept updated separately. */
|
||||
if (b_v3d) {
|
||||
assert(scene->need_motion() == Scene::MOTION_NONE ||
|
||||
scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Insert motion times from camera. Motion times from other objects
|
||||
* have already been added in a sync_objects call. */
|
||||
if (b_cam) {
|
||||
const uint camera_motion_steps = object_motion_steps(*b_cam, *b_cam);
|
||||
for (size_t step = 0; step < camera_motion_steps; step++) {
|
||||
motion_times.insert(scene->camera->motion_time(step));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check which geometry already has motion blur so it can be skipped. */
|
||||
geometry_motion_attribute_synced.clear();
|
||||
for (Geometry *geom : scene->geometry) {
|
||||
const Attribute *attr_P = geom->attributes.find(ATTR_STD_POSITION);
|
||||
if (attr_P && attr_P->has_motion()) {
|
||||
geometry_motion_attribute_synced.insert(geom);
|
||||
}
|
||||
}
|
||||
|
||||
/* note iteration over motion_times set happens in sorted order */
|
||||
for (const float relative_time : motion_times) {
|
||||
/* center time is already handled. */
|
||||
if (relative_time == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DEBUG << "Synchronizing motion for the relative time " << relative_time << ".";
|
||||
|
||||
/* fixed shutter time to get previous and next frame for motion pass */
|
||||
const float shuttertime = scene->motion_shutter_time();
|
||||
|
||||
/* compute frame and subframe time */
|
||||
const float time = frame_center + subframe_center + frame_center_delta +
|
||||
relative_time * shuttertime * 0.5f;
|
||||
const int frame = (int)floorf(time);
|
||||
const float subframe = time - frame;
|
||||
|
||||
/* change frame */
|
||||
python_thread_state_restore(python_thread_state);
|
||||
RE_engine_frame_set(b_engine, frame, subframe);
|
||||
python_thread_state_save(python_thread_state);
|
||||
|
||||
/* Syncs camera motion if relative_time is one of the camera's motion times. */
|
||||
sync_camera_motion(b_render, b_cam, width, height, relative_time);
|
||||
|
||||
/* sync object */
|
||||
sync_objects(b_depsgraph, b_screen, b_v3d, relative_time);
|
||||
}
|
||||
|
||||
geometry_motion_attribute_synced.clear();
|
||||
|
||||
/* we need to set the python thread state again because this
|
||||
* function assumes it is being executed from python and will
|
||||
* try to save the thread state */
|
||||
python_thread_state_restore(python_thread_state);
|
||||
RE_engine_frame_set(b_engine, frame_center, subframe_center);
|
||||
python_thread_state_save(python_thread_state);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
144
blender-5.2.0/intern/cycles/blender/object_cull.cpp
Normal file
144
blender-5.2.0/intern/cycles/blender/object_cull.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "scene/camera.h"
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
|
||||
#include "blender/object_cull.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BlenderObjectCulling::BlenderObjectCulling(Scene *scene, blender::Scene &b_scene)
|
||||
: use_scene_camera_cull_(false),
|
||||
use_camera_cull_(false),
|
||||
camera_cull_margin_(0.0f),
|
||||
use_scene_distance_cull_(false),
|
||||
use_distance_cull_(false),
|
||||
distance_cull_margin_(0.0f)
|
||||
{
|
||||
if ((b_scene.r.mode & blender::R_SIMPLIFY) != 0) {
|
||||
blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene.id);
|
||||
blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles");
|
||||
|
||||
const bool cam_supported = (scene->camera->get_camera_type() == CAMERA_PERSPECTIVE) ||
|
||||
(scene->camera->get_camera_type() == CAMERA_ORTHOGRAPHIC);
|
||||
|
||||
use_scene_camera_cull_ = cam_supported && ((b_scene.r.scemode & blender::R_MULTIVIEW) == 0) &&
|
||||
get_boolean(cscene, "use_camera_cull");
|
||||
use_scene_distance_cull_ = cam_supported &&
|
||||
((b_scene.r.scemode & blender::R_MULTIVIEW) == 0) &&
|
||||
get_boolean(cscene, "use_distance_cull");
|
||||
|
||||
camera_cull_margin_ = get_float(cscene, "camera_cull_margin");
|
||||
distance_cull_margin_ = get_float(cscene, "distance_cull_margin");
|
||||
|
||||
if (distance_cull_margin_ == 0.0f) {
|
||||
use_scene_distance_cull_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlenderObjectCulling::init_object(Scene *scene, blender::Object &b_ob)
|
||||
{
|
||||
if (!use_scene_camera_cull_ && !use_scene_distance_cull_) {
|
||||
return;
|
||||
}
|
||||
|
||||
blender::PointerRNA b_ob_rna_ptr = RNA_id_pointer_create(&b_ob.id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&b_ob_rna_ptr, "cycles");
|
||||
|
||||
use_camera_cull_ = use_scene_camera_cull_ && get_boolean(cobject, "use_camera_cull");
|
||||
use_distance_cull_ = use_scene_distance_cull_ && get_boolean(cobject, "use_distance_cull");
|
||||
|
||||
if (use_camera_cull_ || use_distance_cull_) {
|
||||
/* Need to have proper projection matrix. */
|
||||
scene->camera->update(scene);
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderObjectCulling::test(Scene *scene, blender::Object &b_ob, const Transform &tfm)
|
||||
{
|
||||
if (!use_camera_cull_ && !use_distance_cull_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Compute world space bounding box corners. */
|
||||
float3 bb[8];
|
||||
std::array<blender::float3, 8> boundbox;
|
||||
if (const std::optional<blender::Bounds<blender::float3>> bounds =
|
||||
BKE_object_boundbox_eval_cached_get(&b_ob))
|
||||
{
|
||||
boundbox = blender::bounds::corners(*bounds);
|
||||
}
|
||||
else {
|
||||
boundbox.fill(blender::float3(0));
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const float3 p = make_float3(boundbox[i].x, boundbox[i].y, boundbox[i].z);
|
||||
bb[i] = transform_point(&tfm, p);
|
||||
}
|
||||
|
||||
const bool camera_culled = use_camera_cull_ && test_camera(scene, bb);
|
||||
const bool distance_culled = use_distance_cull_ && test_distance(scene, bb);
|
||||
|
||||
return ((camera_culled && distance_culled) || (camera_culled && !use_distance_cull_) ||
|
||||
(distance_culled && !use_camera_cull_));
|
||||
}
|
||||
|
||||
/* TODO(sergey): Not really optimal, consider approaches based on k-DOP in order
|
||||
* to reduce number of objects which are wrongly considered visible.
|
||||
*/
|
||||
bool BlenderObjectCulling::test_camera(Scene *scene, const float3 bb[8])
|
||||
{
|
||||
Camera *cam = scene->camera;
|
||||
const ProjectionTransform &worldtondc = cam->worldtondc;
|
||||
float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
float3 bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
bool all_behind = true;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
float3 p = bb[i];
|
||||
const float4 b = make_float4(p, 1.0f);
|
||||
const float4 c = make_float4(
|
||||
dot(worldtondc.x, b), dot(worldtondc.y, b), dot(worldtondc.z, b), dot(worldtondc.w, b));
|
||||
p = make_float3(c / c.w);
|
||||
if (c.z < 0.0f) {
|
||||
p.x = 1.0f - p.x;
|
||||
p.y = 1.0f - p.y;
|
||||
}
|
||||
if (c.z >= -camera_cull_margin_) {
|
||||
all_behind = false;
|
||||
}
|
||||
bb_min = min(bb_min, p);
|
||||
bb_max = max(bb_max, p);
|
||||
}
|
||||
if (all_behind) {
|
||||
return true;
|
||||
}
|
||||
return (bb_min.x >= 1.0f + camera_cull_margin_ || bb_min.y >= 1.0f + camera_cull_margin_ ||
|
||||
bb_max.x <= -camera_cull_margin_ || bb_max.y <= -camera_cull_margin_);
|
||||
}
|
||||
|
||||
bool BlenderObjectCulling::test_distance(Scene *scene, const float3 bb[8])
|
||||
{
|
||||
const float3 camera_position = transform_get_column(&scene->camera->get_matrix(), 3);
|
||||
float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
float3 bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
|
||||
/* Find min & max points for x & y & z on bounding box */
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const float3 p = bb[i];
|
||||
bb_min = min(bb_min, p);
|
||||
bb_max = max(bb_max, p);
|
||||
}
|
||||
|
||||
const float3 closest_point = max(min(bb_max, camera_position), bb_min);
|
||||
return (len_squared(camera_position - closest_point) >
|
||||
distance_cull_margin_ * distance_cull_margin_);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
33
blender-5.2.0/intern/cycles/blender/object_cull.h
Normal file
33
blender-5.2.0/intern/cycles/blender/object_cull.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "blender/sync.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Scene;
|
||||
|
||||
class BlenderObjectCulling {
|
||||
public:
|
||||
BlenderObjectCulling(Scene *scene, blender::Scene &b_scene);
|
||||
|
||||
void init_object(Scene *scene, blender::Object &b_ob);
|
||||
bool test(Scene *scene, blender::Object &b_ob, const Transform &tfm);
|
||||
|
||||
private:
|
||||
bool test_camera(Scene *scene, const float3 bb[8]);
|
||||
bool test_distance(Scene *scene, const float3 bb[8]);
|
||||
|
||||
bool use_scene_camera_cull_;
|
||||
bool use_camera_cull_;
|
||||
float camera_cull_margin_;
|
||||
bool use_scene_distance_cull_;
|
||||
bool use_distance_cull_;
|
||||
float distance_cull_margin_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
119
blender-5.2.0/intern/cycles/blender/output_driver.cpp
Normal file
119
blender-5.2.0/intern/cycles/blender/output_driver.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "blender/output_driver.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
#include "RE_engine.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BlenderOutputDriver::BlenderOutputDriver(blender::RenderEngine &b_engine) : b_engine_(b_engine) {}
|
||||
|
||||
BlenderOutputDriver::~BlenderOutputDriver() = default;
|
||||
|
||||
bool BlenderOutputDriver::read_render_tile(const Tile &tile)
|
||||
{
|
||||
/* Get render result. */
|
||||
blender::RenderResult *b_rr = RE_engine_begin_result(&b_engine_,
|
||||
tile.offset.x,
|
||||
tile.offset.y,
|
||||
tile.size.x,
|
||||
tile.size.y,
|
||||
tile.layer.c_str(),
|
||||
tile.view.c_str());
|
||||
|
||||
/* Can happen if the intersected rectangle gives 0 width or height. */
|
||||
if (b_rr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* layer will be missing if it was disabled in the UI */
|
||||
if (b_rr->layers.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blender::RenderLayer *b_rlay = static_cast<blender::RenderLayer *>(b_rr->layers.first);
|
||||
|
||||
/* Copy each pass.
|
||||
* TODO:copy only the required ones for better performance? */
|
||||
for (blender::RenderPass &b_pass : b_rlay->passes) {
|
||||
if (b_pass.ibuf && b_pass.ibuf->float_data()) {
|
||||
const float *rect = b_pass.ibuf->float_data();
|
||||
tile.set_pass_pixels(b_pass.name, b_pass.channels, rect);
|
||||
}
|
||||
else {
|
||||
blender::Array<float> rect(int64_t(b_pass.channels) * b_pass.rectx * b_pass.recty, 0.0f);
|
||||
tile.set_pass_pixels(b_pass.name, b_pass.channels, rect.data());
|
||||
}
|
||||
}
|
||||
|
||||
RE_engine_end_result(&b_engine_, b_rr, false, false, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlenderOutputDriver::update_render_tile(const Tile &tile)
|
||||
{
|
||||
/* Use final write for preview renders, otherwise render result wouldn't be updated
|
||||
* quickly on Blender side. For all other cases we use the display driver. */
|
||||
if (b_engine_.flag & blender::RE_ENGINE_PREVIEW) {
|
||||
write_render_tile(tile);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Don't highlight full-frame tile. */
|
||||
if (!(tile.size == tile.full_size)) {
|
||||
RE_engine_tile_highlight_clear_all(&b_engine_);
|
||||
RE_engine_tile_highlight_set(
|
||||
&b_engine_, tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BlenderOutputDriver::write_render_tile(const Tile &tile)
|
||||
{
|
||||
RE_engine_tile_highlight_clear_all(&b_engine_);
|
||||
|
||||
/* Get render result. */
|
||||
blender::RenderResult *b_rr = RE_engine_begin_result(&b_engine_,
|
||||
tile.offset.x,
|
||||
tile.offset.y,
|
||||
tile.size.x,
|
||||
tile.size.y,
|
||||
tile.layer.c_str(),
|
||||
tile.view.c_str());
|
||||
|
||||
/* Can happen if the intersected rectangle gives 0 width or height. */
|
||||
if (b_rr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Layer will be missing if it was disabled in the UI. */
|
||||
if (b_rr->layers.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blender::RenderLayer *b_rlay = static_cast<blender::RenderLayer *>(b_rr->layers.first);
|
||||
|
||||
vector<float> pixels(static_cast<size_t>(tile.size.x) * tile.size.y * 4);
|
||||
|
||||
/* Copy each pass. */
|
||||
for (blender::RenderPass &b_pass : b_rlay->passes) {
|
||||
if (!tile.get_pass_pixels(b_pass.name, b_pass.channels, pixels.data())) {
|
||||
memset(pixels.data(), 0, pixels.size() * sizeof(float));
|
||||
}
|
||||
if (b_pass.ibuf && b_pass.ibuf->float_data()) {
|
||||
float *rect = b_pass.ibuf->float_data_for_write();
|
||||
const size_t size_in_bytes = sizeof(float) * b_pass.rectx * b_pass.recty * b_pass.channels;
|
||||
memcpy(rect, pixels.data(), size_in_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
RE_engine_end_result(&b_engine_, b_rr, false, false, true);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/blender/output_driver.h
Normal file
28
blender-5.2.0/intern/cycles/blender/output_driver.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "session/output_driver.h"
|
||||
|
||||
namespace blender {
|
||||
struct RenderEngine;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BlenderOutputDriver : public OutputDriver {
|
||||
public:
|
||||
explicit BlenderOutputDriver(blender::RenderEngine &b_engine);
|
||||
~BlenderOutputDriver() override;
|
||||
|
||||
void write_render_tile(const Tile &tile) override;
|
||||
bool update_render_tile(const Tile &tile) override;
|
||||
bool read_render_tile(const Tile &tile) override;
|
||||
|
||||
protected:
|
||||
blender::RenderEngine &b_engine_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
94
blender-5.2.0/intern/cycles/blender/particles.cpp
Normal file
94
blender-5.2.0/intern/cycles/blender/particles.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/particles.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "DNA_particle_types.h"
|
||||
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Utilities */
|
||||
|
||||
bool BlenderSync::sync_dupli_particle(blender::Object &b_parent,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
blender::Object &b_ob,
|
||||
Object *object)
|
||||
{
|
||||
/* Test if this dupli was generated from a particle system. */
|
||||
blender::ParticleSystem *b_psys = b_deg_iter_data.dupli_object_current->particle_system;
|
||||
if (!b_psys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
object->set_hide_on_missing_motion(true);
|
||||
|
||||
/* test if we need particle data */
|
||||
if (!object->get_geometry()->need_attribute(scene, ATTR_STD_PARTICLE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* don't handle child particles yet */
|
||||
const int *persistent_id = b_deg_iter_data.dupli_object_current->persistent_id;
|
||||
|
||||
if (persistent_id[0] >= b_psys->totpart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* find particle system */
|
||||
const ParticleSystemKey key(&b_parent, persistent_id);
|
||||
ParticleSystem *psys;
|
||||
|
||||
const bool first_use = !particle_system_map.is_used(key);
|
||||
const bool need_update = particle_system_map.add_or_update(&psys, &b_parent.id, &b_ob.id, key);
|
||||
|
||||
/* no update needed? */
|
||||
if (!need_update && !object->get_geometry()->is_modified() &&
|
||||
!scene->object_manager->need_update())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* first time used in this sync loop? clear and tag update */
|
||||
if (first_use) {
|
||||
psys->particles.clear();
|
||||
psys->tag_update(scene);
|
||||
}
|
||||
|
||||
/* add particle */
|
||||
blender::ParticleData &b_pa = b_psys->particles[persistent_id[0]];
|
||||
Particle pa;
|
||||
|
||||
pa.index = persistent_id[0];
|
||||
pa.age = BKE_scene_frame_to_ctime(b_scene, b_scene->r.cfra) - b_pa.time;
|
||||
pa.lifetime = b_pa.lifetime;
|
||||
pa.location = make_float3(b_pa.state.co[0], b_pa.state.co[1], b_pa.state.co[2]);
|
||||
pa.rotation = make_float4(
|
||||
b_pa.state.rot[0], b_pa.state.rot[1], b_pa.state.rot[2], b_pa.state.rot[3]);
|
||||
pa.size = b_pa.size;
|
||||
pa.velocity = make_float3(b_pa.state.vel[0], b_pa.state.vel[1], b_pa.state.vel[2]);
|
||||
pa.angular_velocity = make_float3(b_pa.state.ave[0], b_pa.state.ave[1], b_pa.state.ave[2]);
|
||||
|
||||
psys->particles.push_back_slow(pa);
|
||||
|
||||
object->set_particle_system(psys);
|
||||
object->set_particle_index(psys->particles.size() - 1);
|
||||
|
||||
if (object->particle_index_is_modified()) {
|
||||
scene->object_manager->tag_update(scene, ObjectManager::PARTICLE_MODIFIED);
|
||||
}
|
||||
|
||||
/* return that this object has particle data */
|
||||
return true;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
318
blender-5.2.0/intern/cycles/blender/pointcloud.cpp
Normal file
318
blender-5.2.0/intern/cycles/blender/pointcloud.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/attribute.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#include "util/hash.h"
|
||||
|
||||
#include "blender/attribute_convert.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static void attr_create_motion_from_velocity(PointCloud *pointcloud,
|
||||
const blender::Span<blender::float3> b_attribute,
|
||||
const float motion_scale)
|
||||
{
|
||||
const int num_points = pointcloud->num_points();
|
||||
|
||||
/* Override motion steps to fixed number. */
|
||||
pointcloud->set_motion_steps(3);
|
||||
|
||||
/* Set motion steps on position and radius attributes. */
|
||||
Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
Attribute *attr_R = pointcloud->attributes.find(ATTR_STD_RADIUS);
|
||||
attr_P->add_motion(pointcloud);
|
||||
attr_R->add_motion(pointcloud);
|
||||
const packed_float3 *P = pointcloud->get_position();
|
||||
const float *radius = pointcloud->get_radius();
|
||||
|
||||
/* Only export previous and next frame, we don't have any in between data. */
|
||||
const float motion_times[2] = {-1.0f, 1.0f};
|
||||
for (int step = 1; step <= 2; step++) {
|
||||
const float relative_time = motion_times[step - 1] * 0.5f * motion_scale;
|
||||
packed_float3 *mP = attr_P->data_for_write<packed_float3>(step);
|
||||
float *mR = attr_R->data_for_write<float>(step);
|
||||
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
mP[i] = float3(P[i]) +
|
||||
make_float3(b_attribute[i][0], b_attribute[i][1], b_attribute[i][2]) * relative_time;
|
||||
mR[i] = radius[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_attributes(PointCloud *pointcloud,
|
||||
const blender::PointCloud &b_pointcloud,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const blender::bke::AttributeAccessor b_attributes = b_pointcloud.attributes();
|
||||
if (b_attributes.domain_size(blender::bke::AttrDomain::Point) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AttributeSet &attributes = pointcloud->attributes;
|
||||
static const ustring u_velocity("velocity");
|
||||
b_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
|
||||
const ustring name{std::string_view(iter.name)};
|
||||
|
||||
if (need_motion && name == u_velocity) {
|
||||
const blender::VArraySpan b_attr = *iter.get<blender::float3>();
|
||||
attr_create_motion_from_velocity(pointcloud, b_attr, motion_scale);
|
||||
}
|
||||
|
||||
if (attributes.find(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const blender::bke::GAttributeReader b_attr = iter.get();
|
||||
blender::bke::attribute_math::to_static_type(b_attr.varray.type(), [&]<typename BlenderT>() {
|
||||
using Converter = typename ccl::AttributeConverter<BlenderT>;
|
||||
using CyclesT = typename Converter::CyclesT;
|
||||
if constexpr (!std::is_void_v<CyclesT>) {
|
||||
const blender::VArray<BlenderT> src_varray = b_attr.varray.typed<BlenderT>();
|
||||
const blender::CommonVArrayInfo info = b_attr.varray.common_info();
|
||||
|
||||
if (info.type == blender::CommonVArrayInfo::Type::Single) {
|
||||
const auto &single_value = *static_cast<const BlenderT *>(info.data);
|
||||
Attribute *attr = attributes.add(name, Converter::type_desc, ATTR_ELEMENT_MESH);
|
||||
CyclesT *data = reinterpret_cast<CyclesT *>(attr->data_for_write());
|
||||
*data = Converter::convert(single_value);
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (Converter::layout_compatible) {
|
||||
if (info.type == blender::CommonVArrayInfo::Type::Span && b_attr.sharing_info) {
|
||||
attributes.add_shared(name,
|
||||
Converter::type_desc,
|
||||
ATTR_ELEMENT_VERTEX,
|
||||
info.data,
|
||||
src_varray.size(),
|
||||
b_attr.sharing_info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Attribute *attr = attributes.add(name, Converter::type_desc, ATTR_ELEMENT_VERTEX);
|
||||
CyclesT *data = reinterpret_cast<CyclesT *>(attr->data_for_write());
|
||||
|
||||
const blender::VArraySpan src = src_varray;
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static void export_pointcloud(Scene *scene,
|
||||
PointCloud *pointcloud,
|
||||
const blender::PointCloud &b_pointcloud,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const blender::Span<blender::float3> b_positions = b_pointcloud.positions();
|
||||
const blender::bke::AttributeAccessor b_attributes = b_pointcloud.attributes();
|
||||
|
||||
pointcloud->resize(b_positions.size());
|
||||
|
||||
/* Sync positions, sharing with Blender when possible. */
|
||||
sync_attribute_from_blender(
|
||||
pointcloud->attributes,
|
||||
ATTR_STD_POSITION,
|
||||
b_attributes.lookup<blender::float3>("position", blender::bke::AttrDomain::Point),
|
||||
b_positions.size());
|
||||
pointcloud->tag_position_modified();
|
||||
|
||||
/* Sync radius, sharing with Blender when possible, or filling default. */
|
||||
if (sync_attribute_from_blender(
|
||||
pointcloud->attributes,
|
||||
ATTR_STD_RADIUS,
|
||||
b_attributes.lookup<float>("radius", blender::bke::AttrDomain::Point),
|
||||
b_positions.size()))
|
||||
{
|
||||
pointcloud->tag_radius_modified();
|
||||
}
|
||||
else {
|
||||
float *radius = pointcloud->get_radius_for_write();
|
||||
std::fill(radius, radius + b_positions.size(), 0.01f);
|
||||
}
|
||||
|
||||
int *shader = pointcloud->get_shader().data();
|
||||
std::fill(shader, shader + b_positions.size(), 0);
|
||||
|
||||
if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) {
|
||||
Attribute *attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM);
|
||||
float *data = attr_random->data_for_write<float>();
|
||||
for (const int i : b_positions.index_range()) {
|
||||
data[i] = hash_uint2_to_float(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
copy_attributes(pointcloud, b_pointcloud, need_motion, motion_scale);
|
||||
}
|
||||
|
||||
static void export_pointcloud_motion(PointCloud *pointcloud,
|
||||
const blender::PointCloud &b_pointcloud,
|
||||
const int motion_step)
|
||||
{
|
||||
/* Set motion steps on position and radius attributes. */
|
||||
Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
Attribute *attr_R = pointcloud->attributes.find(ATTR_STD_RADIUS);
|
||||
bool new_attribute = false;
|
||||
|
||||
if (!attr_P->has_motion()) {
|
||||
attr_P->add_motion(pointcloud);
|
||||
attr_R->add_motion(pointcloud);
|
||||
new_attribute = true;
|
||||
}
|
||||
|
||||
const int num_points = pointcloud->num_points();
|
||||
const int attr_step = motion_step + 1;
|
||||
const blender::Span<blender::float3> b_positions = b_pointcloud.positions();
|
||||
const blender::bke::AttributeAccessor b_attributes = b_pointcloud.attributes();
|
||||
const bool size_matches = (b_positions.size() == num_points);
|
||||
|
||||
bool have_motion = false;
|
||||
|
||||
if (size_matches) {
|
||||
/* Fast path: point count unchanged, sync the whole step from Blender,
|
||||
* sharing the buffer when possible. */
|
||||
sync_attribute_motion_step_from_blender(
|
||||
*attr_P,
|
||||
attr_step,
|
||||
b_attributes.lookup<blender::float3>("position", blender::bke::AttrDomain::Point));
|
||||
if (!sync_attribute_motion_step_from_blender(
|
||||
*attr_R,
|
||||
attr_step,
|
||||
b_attributes.lookup<float>("radius", blender::bke::AttrDomain::Point)))
|
||||
{
|
||||
float *mR = attr_R->data_for_write<float>(attr_step);
|
||||
std::fill(mR, mR + num_points, 0.01f);
|
||||
}
|
||||
|
||||
/* If the buffer is shared from Blender and unchanged across frames, the
|
||||
* pointer matches the center step's, so the memcmp is skipped. */
|
||||
const packed_float3 *motion_P = attr_P->data<packed_float3>(attr_step);
|
||||
const packed_float3 *center_P = pointcloud->get_position();
|
||||
have_motion = motion_P != center_P &&
|
||||
std::memcmp(motion_P, center_P, num_points * sizeof(packed_float3)) != 0;
|
||||
}
|
||||
else {
|
||||
/* Slow path: point count differs, copy what overlaps. */
|
||||
const blender::VArraySpan b_radius = *b_attributes.lookup<float>(
|
||||
"radius", blender::bke::AttrDomain::Point);
|
||||
packed_float3 *mP = attr_P->data_for_write<packed_float3>(attr_step);
|
||||
float *mR = attr_R->data_for_write<float>(attr_step);
|
||||
for (int i = 0; i < std::min<int>(num_points, b_positions.size()); i++) {
|
||||
mP[i] = make_float3(b_positions[i][0], b_positions[i][1], b_positions[i][2]);
|
||||
mR[i] = b_radius.is_empty() ? 0.01f : b_radius[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of new attribute, verify if there really was any motion. */
|
||||
if (new_attribute) {
|
||||
if (!size_matches || !have_motion) {
|
||||
attr_P->remove_motion();
|
||||
attr_R->remove_motion();
|
||||
}
|
||||
else if (motion_step > 0) {
|
||||
/* Motion, fill up previous steps that we might have skipped because
|
||||
* they had no motion, but we need them anyway now. */
|
||||
for (int step = 0; step < motion_step; step++) {
|
||||
pointcloud->copy_center_to_motion_step(step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Export attributes */
|
||||
copy_attributes(pointcloud, b_pointcloud, false, 0.0f);
|
||||
}
|
||||
|
||||
void BlenderSync::sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info)
|
||||
{
|
||||
const size_t old_numpoints = pointcloud->num_points();
|
||||
|
||||
array<Node *> used_shaders = pointcloud->get_used_shaders();
|
||||
|
||||
PointCloud new_pointcloud;
|
||||
new_pointcloud.set_used_shaders(used_shaders);
|
||||
|
||||
/* TODO: add option to filter out points in the view layer. */
|
||||
const blender::PointCloud *b_pointcloud = blender::id_cast<blender::PointCloud *>(
|
||||
b_ob_info.object_data);
|
||||
/* Motion blur attribute is relative to seconds, we need it relative to frames. */
|
||||
const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
|
||||
const float motion_scale = (need_motion) ? scene->motion_shutter_time() /
|
||||
(b_scene->r.frs_sec / b_scene->r.frs_sec_base) :
|
||||
0.0f;
|
||||
export_pointcloud(scene, &new_pointcloud, *b_pointcloud, need_motion, motion_scale);
|
||||
|
||||
if (scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE &&
|
||||
pointcloud->num_points() == new_pointcloud.num_points())
|
||||
{
|
||||
new_pointcloud.set_motion_steps(2);
|
||||
|
||||
Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
Attribute *new_attr_P = new_pointcloud.attributes.find(ATTR_STD_POSITION);
|
||||
if (attr_P->has_motion()) {
|
||||
new_attr_P->take_motion_from(*attr_P);
|
||||
}
|
||||
else {
|
||||
new_attr_P->add_motion(&new_pointcloud);
|
||||
new_pointcloud.copy_center_to_motion_step(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update original sockets. */
|
||||
|
||||
pointcloud->clear_non_sockets();
|
||||
|
||||
for (const SocketType &socket : new_pointcloud.type->inputs) {
|
||||
/* Those sockets are updated in sync_object, so do not modify them. */
|
||||
if (socket.name == "use_motion_blur" || socket.name == "used_shaders") {
|
||||
continue;
|
||||
}
|
||||
pointcloud->set_value(socket, new_pointcloud, socket);
|
||||
}
|
||||
|
||||
pointcloud->attributes.update(std::move(new_pointcloud.attributes));
|
||||
|
||||
/* Tag update. */
|
||||
const bool rebuild = (pointcloud && old_numpoints != pointcloud->num_points());
|
||||
pointcloud->tag_update(scene, rebuild);
|
||||
}
|
||||
|
||||
void BlenderSync::sync_pointcloud_motion(PointCloud *pointcloud,
|
||||
BObjectInfo &b_ob_info,
|
||||
const int motion_step)
|
||||
{
|
||||
/* Skip if nothing exported. */
|
||||
if (pointcloud->num_points() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Export deformed coordinates. */
|
||||
if (ccl::BKE_object_is_deform_modified(b_ob_info, *b_scene, preview)) {
|
||||
/* PointCloud object. */
|
||||
const blender::PointCloud *b_pointcloud = blender::id_cast<blender::PointCloud *>(
|
||||
b_ob_info.object_data);
|
||||
export_pointcloud_motion(pointcloud, *b_pointcloud, motion_step);
|
||||
}
|
||||
else {
|
||||
/* No deformation on this frame, copy coordinates if other frames did have it. */
|
||||
pointcloud->copy_center_to_motion_step(motion_step);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
939
blender-5.2.0/intern/cycles/blender/python.cpp
Normal file
939
blender-5.2.0/intern/cycles/blender/python.cpp
Normal file
@@ -0,0 +1,939 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "blender/CCL_api.h"
|
||||
|
||||
#include "blender/device.h"
|
||||
#include "blender/session.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "session/denoising.h"
|
||||
#include "session/merge.h"
|
||||
|
||||
#include "util/colorspace.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/guiding.h"
|
||||
#include "util/image_maketx.h"
|
||||
#include "util/image_metadata.h"
|
||||
#include "util/log.h"
|
||||
#include "util/openimagedenoise.h"
|
||||
#include "util/path.h"
|
||||
#include "util/string.h"
|
||||
#include "util/task.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "scene/osl.h"
|
||||
|
||||
#ifdef WITH_METAL
|
||||
# include "device/metal/device.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
/* Flag describing whether debug flags were synchronized from scene. */
|
||||
bool debug_flags_set = false;
|
||||
|
||||
void *pylong_as_voidptr_typesafe(PyObject *object)
|
||||
{
|
||||
if (object == Py_None) {
|
||||
return nullptr;
|
||||
}
|
||||
return PyLong_AsVoidPtr(object);
|
||||
}
|
||||
|
||||
PyObject *pyunicode_from_string(const char *str)
|
||||
{
|
||||
/* Ignore errors if device API returns invalid UTF8 strings. */
|
||||
return PyUnicode_DecodeUTF8(str, strlen(str), "ignore");
|
||||
}
|
||||
|
||||
/* Synchronize debug flags from a given Blender scene.
|
||||
* Return truth when device list needs invalidation.
|
||||
*/
|
||||
void debug_flags_sync_from_scene(blender::Scene &b_scene)
|
||||
{
|
||||
DebugFlagsRef flags = DebugFlags();
|
||||
blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene.id);
|
||||
blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles");
|
||||
/* Synchronize CPU flags. */
|
||||
flags.cpu.avx2 = get_boolean(cscene, "debug_use_cpu_avx2");
|
||||
flags.cpu.sse42 = get_boolean(cscene, "debug_use_cpu_sse42");
|
||||
flags.cpu.bvh_layout = (BVHLayout)get_enum(cscene, "debug_bvh_layout");
|
||||
/* Synchronize CUDA flags. */
|
||||
flags.cuda.adaptive_compile = get_boolean(cscene, "debug_use_cuda_adaptive_compile");
|
||||
flags.hip.adaptive_compile = get_boolean(cscene, "debug_use_hip_adaptive_compile");
|
||||
flags.metal.adaptive_compile = get_boolean(cscene, "debug_use_metal_adaptive_compile");
|
||||
/* Synchronize OptiX flags. */
|
||||
flags.optix.use_debug = get_boolean(cscene, "debug_use_optix_debug");
|
||||
/* Synchronize Texture Cache flags. */
|
||||
flags.texture_cache.use_eviction = get_boolean(cscene, "debug_use_texture_cache_eviction");
|
||||
flags.texture_cache.preserve_unused = get_int(cscene, "debug_texture_cache_preserve_unused");
|
||||
}
|
||||
|
||||
/* Reset debug flags to default values.
|
||||
* Return truth when device list needs invalidation.
|
||||
*/
|
||||
void debug_flags_reset()
|
||||
{
|
||||
DebugFlagsRef flags = DebugFlags();
|
||||
flags.reset();
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
void python_thread_state_save(void **python_thread_state)
|
||||
{
|
||||
*python_thread_state = (void *)PyEval_SaveThread();
|
||||
}
|
||||
|
||||
void python_thread_state_restore(void **python_thread_state)
|
||||
{
|
||||
PyEval_RestoreThread((PyThreadState *)*python_thread_state);
|
||||
*python_thread_state = nullptr;
|
||||
}
|
||||
|
||||
static const char *PyC_UnicodeAsBytes(PyObject *py_str, PyObject **coerce)
|
||||
{
|
||||
const char *result = PyUnicode_AsUTF8(py_str);
|
||||
if (result) {
|
||||
/* 99% of the time this is enough but we better support non unicode
|
||||
* chars since blender doesn't limit this.
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
PyErr_Clear();
|
||||
if (PyBytes_Check(py_str)) {
|
||||
return PyBytes_AS_STRING(py_str);
|
||||
}
|
||||
|
||||
*coerce = PyUnicode_EncodeFSDefault(py_str);
|
||||
if (*coerce) {
|
||||
return PyBytes_AS_STRING(*coerce);
|
||||
}
|
||||
/* Clear the error, so Cycles can be at least used without
|
||||
* GPU and OSL support,
|
||||
*/
|
||||
PyErr_Clear();
|
||||
return "";
|
||||
}
|
||||
|
||||
static PyObject *init_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *path;
|
||||
PyObject *user_path;
|
||||
int headless;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OOi", &path, &user_path, &headless)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject *path_coerce = nullptr;
|
||||
PyObject *user_path_coerce = nullptr;
|
||||
path_init(PyC_UnicodeAsBytes(path, &path_coerce),
|
||||
PyC_UnicodeAsBytes(user_path, &user_path_coerce));
|
||||
Py_XDECREF(path_coerce);
|
||||
Py_XDECREF(user_path_coerce);
|
||||
|
||||
BlenderSession::headless = headless;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *exit_func(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
#ifdef WITH_METAL
|
||||
device_metal_exit();
|
||||
#endif
|
||||
|
||||
ColorSpaceManager::free_memory();
|
||||
OSLManager::free_memory();
|
||||
TaskScheduler::free_memory();
|
||||
Device::free_memory();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *create_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
PyObject *pypreferences;
|
||||
PyObject *pydata;
|
||||
PyObject *pyscreen;
|
||||
PyObject *pyregion;
|
||||
PyObject *pyv3d;
|
||||
PyObject *pyrv3d;
|
||||
int preview_osl;
|
||||
|
||||
if (!PyArg_ParseTuple(args,
|
||||
"OOOOOOOi",
|
||||
&pyengine,
|
||||
&pypreferences,
|
||||
&pydata,
|
||||
&pyscreen,
|
||||
&pyregion,
|
||||
&pyv3d,
|
||||
&pyrv3d,
|
||||
&preview_osl))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* RNA */
|
||||
blender::ID *bScreen = (blender::ID *)PyLong_AsVoidPtr(pyscreen);
|
||||
|
||||
blender::RenderEngine *engine = static_cast<blender::RenderEngine *>(PyLong_AsVoidPtr(pyengine));
|
||||
blender::UserDef *preferences = static_cast<blender::UserDef *>(PyLong_AsVoidPtr(pypreferences));
|
||||
blender::Main *data = static_cast<blender::Main *>(PyLong_AsVoidPtr(pydata));
|
||||
blender::View3D *v3d = static_cast<blender::View3D *>(pylong_as_voidptr_typesafe(pyv3d));
|
||||
blender::ARegion *region = static_cast<blender::ARegion *>(pylong_as_voidptr_typesafe(pyregion));
|
||||
|
||||
/* create session */
|
||||
BlenderSession *session;
|
||||
|
||||
if (region) {
|
||||
blender::RegionView3D *rv3d = static_cast<blender::RegionView3D *>(region->regiondata);
|
||||
/* interactive viewport session */
|
||||
const int width = region->winx;
|
||||
const int height = region->winy;
|
||||
|
||||
session = new BlenderSession(*engine,
|
||||
*preferences,
|
||||
*data,
|
||||
blender::id_cast<blender::bScreen *>(bScreen),
|
||||
v3d,
|
||||
rv3d,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
else {
|
||||
/* offline session or preview render */
|
||||
session = new BlenderSession(*engine, *preferences, *data, preview_osl);
|
||||
}
|
||||
|
||||
return PyLong_FromVoidPtr(session);
|
||||
}
|
||||
|
||||
static PyObject *free_func(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
delete (BlenderSession *)PyLong_AsVoidPtr(value);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *render_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pysession;
|
||||
PyObject *pydepsgraph;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OO", &pysession, &pydepsgraph)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
|
||||
blender::Depsgraph *b_depsgraph = static_cast<blender::Depsgraph *>(
|
||||
PyLong_AsVoidPtr(pydepsgraph));
|
||||
|
||||
/* Allow Blender to execute other Python scripts. */
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->render(*b_depsgraph);
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *render_frame_finish_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pysession;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &pysession)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
|
||||
|
||||
/* Allow Blender to execute other Python scripts. */
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->render_frame_finish();
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *draw_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *py_session;
|
||||
PyObject *py_graph;
|
||||
PyObject *py_screen;
|
||||
PyObject *py_space_image;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OOOO", &py_session, &py_graph, &py_screen, &py_space_image)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(py_session);
|
||||
|
||||
blender::ID *b_screen = (blender::ID *)PyLong_AsVoidPtr(py_screen);
|
||||
|
||||
blender::SpaceImage *b_space_image = static_cast<blender::SpaceImage *>(
|
||||
pylong_as_voidptr_typesafe(py_space_image));
|
||||
|
||||
session->draw(blender::id_cast<blender::bScreen &>(*b_screen), *b_space_image);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* pixel_array and result passed as pointers */
|
||||
static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pysession;
|
||||
PyObject *pydepsgraph;
|
||||
PyObject *pyobject;
|
||||
const char *pass_type;
|
||||
int pass_filter;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (!PyArg_ParseTuple(args,
|
||||
"OOOsiii",
|
||||
&pysession,
|
||||
&pydepsgraph,
|
||||
&pyobject,
|
||||
&pass_type,
|
||||
&pass_filter,
|
||||
&width,
|
||||
&height))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
|
||||
|
||||
blender::Depsgraph *b_depsgraph = static_cast<blender::Depsgraph *>(
|
||||
PyLong_AsVoidPtr(pydepsgraph));
|
||||
blender::Object *b_object = static_cast<blender::Object *>(PyLong_AsVoidPtr(pyobject));
|
||||
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->bake(*b_depsgraph, *b_object, pass_type, pass_filter, width, height);
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *view_draw_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pysession;
|
||||
PyObject *pygraph;
|
||||
PyObject *pyv3d;
|
||||
PyObject *pyrv3d;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OOOO", &pysession, &pygraph, &pyv3d, &pyrv3d)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
|
||||
|
||||
if (PyLong_AsVoidPtr(pyrv3d)) {
|
||||
/* 3d view drawing */
|
||||
int viewport[4];
|
||||
blender::GPU_viewport_size_get_i(viewport);
|
||||
|
||||
session->view_draw(viewport[2], viewport[3]);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *reset_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pysession;
|
||||
PyObject *pydata;
|
||||
PyObject *pydepsgraph;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OOO", &pysession, &pydata, &pydepsgraph)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
|
||||
|
||||
blender::Main *b_data = static_cast<blender::Main *>(PyLong_AsVoidPtr(pydata));
|
||||
blender::Depsgraph *b_depsgraph = static_cast<blender::Depsgraph *>(
|
||||
PyLong_AsVoidPtr(pydepsgraph));
|
||||
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->reset_session(*b_data, *b_depsgraph);
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *sync_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pysession;
|
||||
PyObject *pydepsgraph;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OO", &pysession, &pydepsgraph)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
|
||||
|
||||
blender::Depsgraph *b_depsgraph = static_cast<blender::Depsgraph *>(
|
||||
PyLong_AsVoidPtr(pydepsgraph));
|
||||
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->synchronize(*b_depsgraph);
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *type_name;
|
||||
if (!PyArg_ParseTuple(args, "s", &type_name)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DeviceType type = Device::type_from_string(type_name);
|
||||
/* "NONE" is defined by the add-on, see: `CyclesPreferences.get_device_types`. */
|
||||
if ((type == DEVICE_NONE) && (strcmp(type_name, "NONE") != 0)) {
|
||||
PyErr_Format(PyExc_ValueError, "Device \"%s\" not known.", type_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint mask = (type == DEVICE_NONE) ? DEVICE_MASK_ALL : DEVICE_MASK(type);
|
||||
mask |= DEVICE_MASK_CPU;
|
||||
|
||||
vector<DeviceInfo> devices = Device::available_devices(mask);
|
||||
PyObject *ret = PyTuple_New(devices.size());
|
||||
|
||||
for (size_t i = 0; i < devices.size(); i++) {
|
||||
const DeviceInfo &device = devices[i];
|
||||
const string type_name = Device::string_from_type(device.type);
|
||||
PyObject *device_tuple = PyTuple_New(8);
|
||||
PyTuple_SET_ITEM(device_tuple, 0, pyunicode_from_string(device.description.c_str()));
|
||||
PyTuple_SET_ITEM(device_tuple, 1, pyunicode_from_string(type_name.c_str()));
|
||||
PyTuple_SET_ITEM(device_tuple, 2, pyunicode_from_string(device.id.c_str()));
|
||||
PyTuple_SET_ITEM(device_tuple, 3, PyBool_FromLong(device.has_peer_memory));
|
||||
PyTuple_SET_ITEM(device_tuple, 4, PyBool_FromLong(device.use_hardware_raytracing));
|
||||
PyTuple_SET_ITEM(
|
||||
device_tuple, 5, PyBool_FromLong(device.denoisers & DENOISER_OPENIMAGEDENOISE));
|
||||
PyTuple_SET_ITEM(device_tuple, 6, PyBool_FromLong(device.denoisers & DENOISER_OPTIX));
|
||||
PyTuple_SET_ITEM(device_tuple, 7, PyBool_FromLong(device.has_execution_optimization));
|
||||
PyTuple_SET_ITEM(ret, i, device_tuple);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef WITH_OSL
|
||||
|
||||
static PyObject *osl_compile_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *inputfile = nullptr;
|
||||
const char *outputfile = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss", &inputfile, &outputfile)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* return */
|
||||
if (!OSLManager::osl_compile(inputfile, outputfile)) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *system_info_func(PyObject * /*self*/, PyObject * /*value*/)
|
||||
{
|
||||
const string system_info = Device::device_capabilities();
|
||||
return pyunicode_from_string(system_info.c_str());
|
||||
}
|
||||
|
||||
static bool image_parse_filepaths(PyObject *pyfilepaths, vector<string> &filepaths)
|
||||
{
|
||||
if (PyUnicode_Check(pyfilepaths)) {
|
||||
const char *filepath = PyUnicode_AsUTF8(pyfilepaths);
|
||||
filepaths.push_back(filepath);
|
||||
return true;
|
||||
}
|
||||
|
||||
PyObject *sequence = PySequence_Fast(pyfilepaths,
|
||||
"File paths must be a string or sequence of strings");
|
||||
if (sequence == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(sequence); i++) {
|
||||
PyObject *item = PySequence_Fast_GET_ITEM(sequence, i);
|
||||
const char *filepath = PyUnicode_AsUTF8(item);
|
||||
if (filepath == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "File paths must be a string or sequence of strings.");
|
||||
Py_DECREF(sequence);
|
||||
return false;
|
||||
}
|
||||
filepaths.push_back(filepath);
|
||||
}
|
||||
Py_DECREF(sequence);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
|
||||
{
|
||||
static const char *keyword_list[] = {
|
||||
"preferences", "scene", "view_layer", "input", "output", nullptr};
|
||||
PyObject *pypreferences;
|
||||
PyObject *pyscene;
|
||||
PyObject *pyviewlayer;
|
||||
PyObject *pyinput;
|
||||
PyObject *pyoutput = nullptr;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args,
|
||||
keywords,
|
||||
"OOOO|O",
|
||||
(char **)keyword_list,
|
||||
&pypreferences,
|
||||
&pyscene,
|
||||
&pyviewlayer,
|
||||
&pyinput,
|
||||
&pyoutput))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Get device specification from preferences and scene. */
|
||||
blender::UserDef *b_preferences = static_cast<blender::UserDef *>(
|
||||
PyLong_AsVoidPtr(pypreferences));
|
||||
blender::Scene *b_scene = static_cast<blender::Scene *>(PyLong_AsVoidPtr(pyscene));
|
||||
|
||||
DeviceInfo preferences_device;
|
||||
const DeviceInfo pathtrace_device = blender_device_info(
|
||||
*b_preferences, *b_scene, true, true, preferences_device);
|
||||
|
||||
/* Get denoising parameters from view layer. */
|
||||
blender::ViewLayer *b_view_layer = static_cast<blender::ViewLayer *>(
|
||||
PyLong_AsVoidPtr(pyviewlayer));
|
||||
|
||||
DenoiseParams params = BlenderSync::get_denoise_params(
|
||||
*b_scene, b_view_layer, true, preferences_device);
|
||||
params.use = true;
|
||||
|
||||
/* Parse file paths list. */
|
||||
vector<string> input;
|
||||
vector<string> output;
|
||||
|
||||
if (!image_parse_filepaths(pyinput, input)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pyoutput) {
|
||||
if (!image_parse_filepaths(pyoutput, output)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
output = input;
|
||||
}
|
||||
|
||||
if (input.empty()) {
|
||||
PyErr_SetString(PyExc_ValueError, "No input file paths specified.");
|
||||
return nullptr;
|
||||
}
|
||||
if (input.size() != output.size()) {
|
||||
PyErr_SetString(PyExc_ValueError, "Number of input and output file paths does not match.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Create denoiser. */
|
||||
/* We are using preference device here, because path trace device will be identical to it unless
|
||||
* scene is setting CPU render or command line override render device. But both of this options
|
||||
* are for render, not for denoising. */
|
||||
DenoiserPipeline denoiser(preferences_device, params);
|
||||
denoiser.input = input;
|
||||
denoiser.output = output;
|
||||
|
||||
/* Run denoiser. */
|
||||
if (!denoiser.run()) {
|
||||
PyErr_SetString(PyExc_ValueError, denoiser.error.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *merge_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
|
||||
{
|
||||
static const char *keyword_list[] = {"input", "output", nullptr};
|
||||
PyObject *pyinput;
|
||||
PyObject *pyoutput = nullptr;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args, keywords, "OO", (char **)keyword_list, &pyinput, &pyoutput))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Parse input list. */
|
||||
vector<string> input;
|
||||
if (!image_parse_filepaths(pyinput, input)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Parse output string. */
|
||||
if (!PyUnicode_Check(pyoutput)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Output must be a string.");
|
||||
return nullptr;
|
||||
}
|
||||
const string output = PyUnicode_AsUTF8(pyoutput);
|
||||
|
||||
/* Merge. */
|
||||
ImageMerger merger;
|
||||
merger.input = input;
|
||||
merger.output = output;
|
||||
|
||||
if (!merger.run()) {
|
||||
PyErr_SetString(PyExc_ValueError, merger.error.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyscene;
|
||||
if (!PyArg_ParseTuple(args, "O", &pyscene)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
blender::Scene *b_scene = static_cast<blender::Scene *>(PyLong_AsVoidPtr(pyscene));
|
||||
|
||||
debug_flags_sync_from_scene(*b_scene);
|
||||
|
||||
debug_flags_set = true;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
debug_flags_reset();
|
||||
if (debug_flags_set) {
|
||||
debug_flags_set = false;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
BlenderSession::print_render_stats = true;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
const vector<DeviceType> device_types = Device::available_types();
|
||||
bool has_cuda = false;
|
||||
bool has_optix = false;
|
||||
bool has_hip = false;
|
||||
bool has_metal = false;
|
||||
bool has_oneapi = false;
|
||||
bool has_hiprt = false;
|
||||
for (const DeviceType device_type : device_types) {
|
||||
has_cuda |= (device_type == DEVICE_CUDA);
|
||||
has_optix |= (device_type == DEVICE_OPTIX);
|
||||
has_hip |= (device_type == DEVICE_HIP);
|
||||
has_metal |= (device_type == DEVICE_METAL);
|
||||
has_oneapi |= (device_type == DEVICE_ONEAPI);
|
||||
has_hiprt |= (device_type == DEVICE_HIPRT);
|
||||
}
|
||||
PyObject *list = PyTuple_New(6);
|
||||
PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda));
|
||||
PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix));
|
||||
PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_hip));
|
||||
PyTuple_SET_ITEM(list, 3, PyBool_FromLong(has_metal));
|
||||
PyTuple_SET_ITEM(list, 4, PyBool_FromLong(has_oneapi));
|
||||
PyTuple_SET_ITEM(list, 5, PyBool_FromLong(has_hiprt));
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg)
|
||||
{
|
||||
PyObject *override_string = PyObject_Str(arg);
|
||||
string override = PyUnicode_AsUTF8(override_string);
|
||||
Py_DECREF(override_string);
|
||||
|
||||
bool include_cpu = false;
|
||||
const string cpu_suffix = "+CPU";
|
||||
if (string_endswith(override, cpu_suffix)) {
|
||||
include_cpu = true;
|
||||
override = override.substr(0, override.length() - cpu_suffix.length());
|
||||
}
|
||||
|
||||
if (override == "CPU") {
|
||||
BlenderSession::device_override = DEVICE_MASK_CPU;
|
||||
}
|
||||
else if (override == "CUDA") {
|
||||
BlenderSession::device_override = DEVICE_MASK_CUDA;
|
||||
}
|
||||
else if (override == "OPTIX") {
|
||||
BlenderSession::device_override = DEVICE_MASK_OPTIX;
|
||||
}
|
||||
else if (override == "HIP") {
|
||||
BlenderSession::device_override = DEVICE_MASK_HIP;
|
||||
}
|
||||
else if (override == "METAL") {
|
||||
BlenderSession::device_override = DEVICE_MASK_METAL;
|
||||
}
|
||||
else if (override == "ONEAPI") {
|
||||
BlenderSession::device_override = DEVICE_MASK_ONEAPI;
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << override << " is not a valid Cycles device.";
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (include_cpu) {
|
||||
BlenderSession::device_override = (DeviceTypeMask)(BlenderSession::device_override |
|
||||
DEVICE_MASK_CPU);
|
||||
}
|
||||
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
|
||||
static PyObject *maketx_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
|
||||
{
|
||||
static const char *keyword_list[] = {
|
||||
"filepath", "colorspace", "alpha_type", "cache_dir", nullptr};
|
||||
|
||||
const char *filepath = nullptr;
|
||||
const char *colorspace = "auto";
|
||||
const char *alpha_type_str = "auto";
|
||||
const char *cache_dir = "";
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args,
|
||||
keywords,
|
||||
"s|sss",
|
||||
(char **)keyword_list,
|
||||
&filepath,
|
||||
&colorspace,
|
||||
&alpha_type_str,
|
||||
&cache_dir))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Alpha type. */
|
||||
ImageAlphaType alpha_type;
|
||||
if (strcmp(alpha_type_str, "straight") == 0) {
|
||||
alpha_type = IMAGE_ALPHA_UNASSOCIATED;
|
||||
}
|
||||
else if (strcmp(alpha_type_str, "premultiplied") == 0) {
|
||||
alpha_type = IMAGE_ALPHA_ASSOCIATED;
|
||||
}
|
||||
else if (strcmp(alpha_type_str, "channel_packed") == 0) {
|
||||
alpha_type = IMAGE_ALPHA_CHANNEL_PACKED;
|
||||
}
|
||||
else if (strcmp(alpha_type_str, "none") == 0) {
|
||||
alpha_type = IMAGE_ALPHA_IGNORE;
|
||||
}
|
||||
else if (strcmp(alpha_type_str, "auto") == 0) {
|
||||
alpha_type = IMAGE_ALPHA_AUTO;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_ValueError, "Unknown alpha type: %s", alpha_type_str);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Colorspace. */
|
||||
const ustring colorspace_ustring = (strcmp(colorspace, "auto") == 0) ? u_colorspace_auto :
|
||||
ustring(colorspace);
|
||||
|
||||
/* Resolve output path, and check if tx file is already up to date. */
|
||||
string out_filepath;
|
||||
ccl::ImageMetaData out_metadata;
|
||||
const bool up_to_date = resolve_tx(filepath,
|
||||
cache_dir,
|
||||
colorspace_ustring,
|
||||
alpha_type,
|
||||
IMAGE_FORMAT_PLAIN,
|
||||
out_filepath,
|
||||
out_metadata);
|
||||
|
||||
if (out_filepath.empty()) {
|
||||
LOG_ERROR << "Source image not found: " << filepath;
|
||||
PyErr_Format(PyExc_RuntimeError, "Source image not found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Generate tx file if needed. */
|
||||
if (!up_to_date) {
|
||||
bool ok;
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
ok = make_tx(filepath, out_filepath, colorspace_ustring, alpha_type, IMAGE_FORMAT_PLAIN);
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
||||
if (!ok) {
|
||||
LOG_ERROR << "Failed to generate tx file";
|
||||
PyErr_Format(PyExc_RuntimeError, "Failed to generate tx file");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return pyunicode_from_string(out_filepath.c_str());
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
{"init", init_func, METH_VARARGS, ""},
|
||||
{"exit", exit_func, METH_VARARGS, ""},
|
||||
{"create", create_func, METH_VARARGS, ""},
|
||||
{"free", free_func, METH_O, ""},
|
||||
{"render", render_func, METH_VARARGS, ""},
|
||||
{"render_frame_finish", render_frame_finish_func, METH_VARARGS, ""},
|
||||
{"draw", draw_func, METH_VARARGS, ""},
|
||||
{"bake", bake_func, METH_VARARGS, ""},
|
||||
{"view_draw", view_draw_func, METH_VARARGS, ""},
|
||||
{"sync", sync_func, METH_VARARGS, ""},
|
||||
{"reset", reset_func, METH_VARARGS, ""},
|
||||
#ifdef WITH_OSL
|
||||
{"osl_compile", osl_compile_func, METH_VARARGS, ""},
|
||||
#endif
|
||||
{"available_devices", available_devices_func, METH_VARARGS, ""},
|
||||
{"system_info", system_info_func, METH_NOARGS, ""},
|
||||
|
||||
/* Standalone denoising */
|
||||
{"denoise", (PyCFunction)denoise_func, METH_VARARGS | METH_KEYWORDS, ""},
|
||||
{"merge", (PyCFunction)merge_func, METH_VARARGS | METH_KEYWORDS, ""},
|
||||
|
||||
/* Debugging routines */
|
||||
{"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""},
|
||||
{"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""},
|
||||
|
||||
/* Statistics. */
|
||||
{"enable_print_stats", enable_print_stats_func, METH_NOARGS, ""},
|
||||
|
||||
/* Compute Device selection */
|
||||
{"get_device_types", get_device_types_func, METH_VARARGS, ""},
|
||||
{"set_device_override", set_device_override_func, METH_O, ""},
|
||||
|
||||
/* Texture cache */
|
||||
{"maketx", (PyCFunction)maketx_func, METH_VARARGS | METH_KEYWORDS, ""},
|
||||
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static struct PyModuleDef module = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "_cycles",
|
||||
/*m_doc*/ "Blender cycles render integration",
|
||||
/*m_size*/ -1,
|
||||
/*m_methods*/ methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
void *blender::CCL_python_module_init()
|
||||
{
|
||||
PyObject *mod = PyModule_Create(&ccl::module);
|
||||
|
||||
#ifdef WITH_OSL
|
||||
/* TODO(sergey): This gives us library we've been linking against.
|
||||
* In theory with dynamic OSL library it might not be
|
||||
* accurate, but there's nothing in OSL API which we
|
||||
* might use to get version in runtime.
|
||||
*/
|
||||
const int curversion = OSL_LIBRARY_VERSION_CODE;
|
||||
PyModule_AddObjectRef(mod, "with_osl", Py_True);
|
||||
PyModule_AddObject(
|
||||
mod,
|
||||
"osl_version",
|
||||
Py_BuildValue("(iii)", curversion / 10000, (curversion / 100) % 100, curversion % 100));
|
||||
PyModule_AddObject(
|
||||
mod,
|
||||
"osl_version_string",
|
||||
PyUnicode_FromFormat(
|
||||
"%2d, %2d, %2d", curversion / 10000, (curversion / 100) % 100, curversion % 100));
|
||||
#else
|
||||
PyModule_AddObjectRef(mod, "with_osl", Py_False);
|
||||
PyModule_AddStringConstant(mod, "osl_version", "unknown");
|
||||
PyModule_AddStringConstant(mod, "osl_version_string", "unknown");
|
||||
#endif
|
||||
|
||||
if (ccl::guiding_supported()) {
|
||||
PyModule_AddObjectRef(mod, "with_path_guiding", Py_True);
|
||||
}
|
||||
else {
|
||||
PyModule_AddObjectRef(mod, "with_path_guiding", Py_False);
|
||||
}
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
PyModule_AddObjectRef(mod, "with_embree", Py_True);
|
||||
#else /* WITH_EMBREE */
|
||||
PyModule_AddObjectRef(mod, "with_embree", Py_False);
|
||||
#endif /* WITH_EMBREE */
|
||||
|
||||
#ifdef WITH_EMBREE_GPU
|
||||
PyModule_AddObjectRef(mod, "with_embree_gpu", Py_True);
|
||||
#else /* WITH_EMBREE_GPU */
|
||||
PyModule_AddObjectRef(mod, "with_embree_gpu", Py_False);
|
||||
#endif /* WITH_EMBREE_GPU */
|
||||
|
||||
if (ccl::openimagedenoise_supported()) {
|
||||
PyModule_AddObjectRef(mod, "with_openimagedenoise", Py_True);
|
||||
}
|
||||
else {
|
||||
PyModule_AddObjectRef(mod, "with_openimagedenoise", Py_False);
|
||||
}
|
||||
|
||||
#ifdef WITH_CYCLES_DEBUG
|
||||
PyModule_AddObjectRef(mod, "with_debug", Py_True);
|
||||
#else /* WITH_CYCLES_DEBUG */
|
||||
PyModule_AddObjectRef(mod, "with_debug", Py_False);
|
||||
#endif /* WITH_CYCLES_DEBUG */
|
||||
|
||||
return (void *)mod;
|
||||
}
|
||||
1138
blender-5.2.0/intern/cycles/blender/session.cpp
Normal file
1138
blender-5.2.0/intern/cycles/blender/session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
166
blender-5.2.0/intern/cycles/blender/session.h
Normal file
166
blender-5.2.0/intern/cycles/blender/session.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "device/device.h"
|
||||
|
||||
#include "scene/scene.h"
|
||||
#include "session/session.h"
|
||||
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
namespace blender {
|
||||
struct bScreen;
|
||||
struct Depsgraph;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct RegionView3D;
|
||||
struct RenderData;
|
||||
struct RenderEngine;
|
||||
struct Scene;
|
||||
struct SpaceImage;
|
||||
struct UserDef;
|
||||
struct View3D;
|
||||
} // namespace blender
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BlenderDisplayDriver;
|
||||
class BlenderSync;
|
||||
class ImageMetaData;
|
||||
class Scene;
|
||||
class Session;
|
||||
|
||||
class BlenderSession {
|
||||
public:
|
||||
BlenderSession(blender::RenderEngine &b_engine,
|
||||
blender::UserDef &b_userpref,
|
||||
blender::Main &b_data,
|
||||
bool preview_osl);
|
||||
|
||||
BlenderSession(blender::RenderEngine &b_engine,
|
||||
blender::UserDef &b_userpref,
|
||||
blender::Main &b_data,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
int height);
|
||||
|
||||
~BlenderSession();
|
||||
|
||||
/* session */
|
||||
void create_session();
|
||||
void free_session();
|
||||
|
||||
void reset_session(blender::Main &b_data, blender::Depsgraph &b_depsgraph);
|
||||
|
||||
/* offline render */
|
||||
void render(blender::Depsgraph &b_depsgraph);
|
||||
|
||||
void render_frame_finish();
|
||||
|
||||
void bake(blender::Depsgraph &b_depsgraph_,
|
||||
blender::Object &b_object,
|
||||
const string &bake_type,
|
||||
const int bake_filter,
|
||||
const int bake_width,
|
||||
const int bake_height);
|
||||
|
||||
void full_buffer_written(string_view filename);
|
||||
/* interactive updates */
|
||||
void synchronize(blender::Depsgraph &b_depsgraph);
|
||||
|
||||
/* drawing */
|
||||
void draw(blender::bScreen &b_screen, blender::SpaceImage &space_image);
|
||||
void view_draw(const int w, const int h);
|
||||
void tag_redraw();
|
||||
void tag_update();
|
||||
void get_status(string &status, string &substatus);
|
||||
void get_progress(double &progress, double &total_time, double &render_time);
|
||||
void test_cancel();
|
||||
void update_status_progress();
|
||||
void update_bake_progress();
|
||||
|
||||
bool background;
|
||||
unique_ptr<Session> session;
|
||||
Scene *scene;
|
||||
unique_ptr<BlenderSync> sync;
|
||||
double last_redraw_time;
|
||||
|
||||
blender::RenderEngine &b_engine;
|
||||
blender::UserDef &b_userpref;
|
||||
blender::Main *b_data;
|
||||
blender::RenderData *b_render;
|
||||
blender::Depsgraph *b_depsgraph;
|
||||
/* NOTE: Blender's scene might become invalid after call
|
||||
* #free_blender_memory_if_possible(). */
|
||||
blender::Scene *b_scene;
|
||||
blender::bScreen *b_screen;
|
||||
blender::View3D *b_v3d;
|
||||
blender::RegionView3D *b_rv3d;
|
||||
string b_rlay_name;
|
||||
string b_rview_name;
|
||||
|
||||
string last_status;
|
||||
string last_error;
|
||||
double last_progress;
|
||||
double last_status_time;
|
||||
|
||||
int width, height;
|
||||
float pixelsize;
|
||||
bool preview_osl;
|
||||
double start_resize_time;
|
||||
|
||||
void *python_thread_state;
|
||||
|
||||
bool use_developer_ui;
|
||||
|
||||
/* Global state which is common for all render sessions created from Blender.
|
||||
* Usually denotes command line arguments.
|
||||
*/
|
||||
static DeviceTypeMask device_override;
|
||||
|
||||
/* Blender is running from the command line, no windows are shown and some
|
||||
* extra render optimization is possible (possible to free draw-only data and
|
||||
* so on.
|
||||
*/
|
||||
static bool headless;
|
||||
|
||||
static bool print_render_stats;
|
||||
|
||||
protected:
|
||||
void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name);
|
||||
|
||||
/* Check whether session error happened.
|
||||
* If so, it is reported to the render engine and true is returned.
|
||||
* Otherwise false is returned. */
|
||||
bool check_and_report_session_error();
|
||||
|
||||
void builtin_images_load();
|
||||
|
||||
/* Is used after each render layer synchronization is done with the goal
|
||||
* of freeing render engine data which is held from Blender side (for
|
||||
* example, dependency graph).
|
||||
*/
|
||||
void free_blender_memory_if_possible();
|
||||
|
||||
void ensure_display_driver_if_needed();
|
||||
|
||||
struct {
|
||||
thread_mutex mutex;
|
||||
int last_pass_index = -1;
|
||||
} draw_state_;
|
||||
|
||||
/* NOTE: The BlenderSession references the display driver. */
|
||||
BlenderDisplayDriver *display_driver_ = nullptr;
|
||||
|
||||
vector<string> full_buffer_files_;
|
||||
|
||||
int bake_id = 0;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
1992
blender-5.2.0/intern/cycles/blender/shader.cpp
Normal file
1992
blender-5.2.0/intern/cycles/blender/shader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1231
blender-5.2.0/intern/cycles/blender/sync.cpp
Normal file
1231
blender-5.2.0/intern/cycles/blender/sync.cpp
Normal file
File diff suppressed because it is too large
Load Diff
308
blender-5.2.0/intern/cycles/blender/sync.h
Normal file
308
blender-5.2.0/intern/cycles/blender/sync.h
Normal file
@@ -0,0 +1,308 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "blender/id_map.h"
|
||||
#include "blender/util.h"
|
||||
#include "blender/viewport.h"
|
||||
|
||||
#include "scene/scene.h"
|
||||
#include "session/session.h"
|
||||
|
||||
#include "util/map.h"
|
||||
#include "util/set.h"
|
||||
|
||||
namespace blender {
|
||||
struct DEGObjectIterData;
|
||||
struct MeshSequenceCacheModifier;
|
||||
} // namespace blender
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Background;
|
||||
class BlenderObjectCulling;
|
||||
class BlenderViewportParameters;
|
||||
class Camera;
|
||||
class Film;
|
||||
class Hair;
|
||||
class Light;
|
||||
class Mesh;
|
||||
class Object;
|
||||
class ParticleSystem;
|
||||
class Scene;
|
||||
class Shader;
|
||||
class ShaderGraph;
|
||||
class TaskPool;
|
||||
|
||||
class BlenderSync {
|
||||
public:
|
||||
BlenderSync(blender::RenderEngine &b_engine,
|
||||
blender::Main &b_data,
|
||||
blender::Scene &b_scene,
|
||||
Scene *scene,
|
||||
bool preview,
|
||||
bool use_developer_ui,
|
||||
Progress &progress);
|
||||
~BlenderSync();
|
||||
|
||||
void reset(blender::Main &b_data, blender::Scene &b_scene);
|
||||
|
||||
void tag_update();
|
||||
|
||||
void set_bake_target(blender::Object &b_object);
|
||||
|
||||
/* sync */
|
||||
void sync_recalc(blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d);
|
||||
void sync_data(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state,
|
||||
const DeviceInfo &denoise_device_info);
|
||||
void sync_view_layer(blender::ViewLayer &b_view_layer);
|
||||
void sync_render_passes(blender::RenderLayer &b_rlay, blender::ViewLayer &b_view_layer);
|
||||
void sync_integrator(blender::ViewLayer &b_view_layer,
|
||||
bool background,
|
||||
const DeviceInfo &denoise_device_info);
|
||||
void sync_scene_attributes();
|
||||
void sync_camera(const blender::RenderData &b_render,
|
||||
const int width,
|
||||
const int height,
|
||||
const char *viewname);
|
||||
void sync_view(blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height);
|
||||
int get_layer_samples()
|
||||
{
|
||||
return view_layer.samples;
|
||||
}
|
||||
int get_layer_bound_samples()
|
||||
{
|
||||
return view_layer.bound_samples;
|
||||
}
|
||||
|
||||
/* Early data free. */
|
||||
void free_data_after_sync(blender::Depsgraph &b_depsgraph);
|
||||
|
||||
/* get parameters */
|
||||
static SceneParams get_scene_params(blender::UserDef &b_preferences,
|
||||
blender::Main &b_data,
|
||||
blender::Scene &b_scene,
|
||||
const bool background,
|
||||
const bool use_developer_ui);
|
||||
static SessionParams get_session_params(blender::RenderEngine &b_engine,
|
||||
blender::UserDef &b_preferences,
|
||||
blender::Scene &b_scene,
|
||||
bool background,
|
||||
float pixelsize);
|
||||
static bool get_session_pause(blender::Scene &b_scene, bool background);
|
||||
static BufferParams get_buffer_params(blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
Camera *cam,
|
||||
const int width,
|
||||
const int height);
|
||||
|
||||
static DenoiseParams get_denoise_params(blender::Scene &b_scene,
|
||||
blender::ViewLayer *b_view_layer,
|
||||
bool background,
|
||||
const DeviceInfo &denoise_device);
|
||||
|
||||
private:
|
||||
/* sync */
|
||||
void sync_lights(blender::Depsgraph &b_depsgraph, bool update_all, bool update_time);
|
||||
void sync_materials(blender::Depsgraph &b_depsgraph, bool update_all, bool update_time);
|
||||
void sync_objects(blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
const float motion_time = 0.0f);
|
||||
void sync_objects_and_motion(blender::RenderData &b_render,
|
||||
blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
blender::RegionView3D *b_rv3d,
|
||||
const int width,
|
||||
const int height,
|
||||
void **python_thread_state);
|
||||
void sync_film(blender::ViewLayer &b_view_layer,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d);
|
||||
void sync_view();
|
||||
|
||||
/* Shader */
|
||||
array<Node *> find_used_shaders(blender::Object &b_ob);
|
||||
void sync_world(blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
bool update_all,
|
||||
bool update_time);
|
||||
void sync_shaders(blender::Depsgraph &b_depsgraph,
|
||||
blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
bool update_all,
|
||||
bool update_time);
|
||||
void sync_nodes(Shader *shader, blender::bNodeTree &b_ntree);
|
||||
|
||||
bool scene_attr_needs_recalc(Shader *shader, blender::Depsgraph &b_depsgraph);
|
||||
void resolve_view_layer_attributes(Shader *shader,
|
||||
ShaderGraph *graph,
|
||||
blender::Depsgraph &b_depsgraph);
|
||||
|
||||
/* Object */
|
||||
Object *sync_object(blender::ViewLayer &b_view_layer,
|
||||
blender::Object &b_ob,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
const float motion_time,
|
||||
bool use_particle_hair,
|
||||
bool show_lights,
|
||||
BlenderObjectCulling &culling,
|
||||
TaskPool *geom_task_pool);
|
||||
void sync_object_motion_init(blender::Object &b_parent, blender::Object &b_ob, Object *object);
|
||||
|
||||
void sync_procedural(blender::Object &b_ob,
|
||||
blender::MeshSequenceCacheModifier &b_mesh_cache,
|
||||
bool has_subdivision);
|
||||
|
||||
bool sync_object_attributes(blender::Object &b_ob,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
Object *object);
|
||||
|
||||
/* Volume */
|
||||
void sync_volume(BObjectInfo &b_ob_info, Volume *volume);
|
||||
|
||||
/* Mesh */
|
||||
void sync_mesh(BObjectInfo &b_ob_info, Mesh *mesh);
|
||||
void sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, int motion_step);
|
||||
|
||||
/* Hair */
|
||||
void sync_hair(BObjectInfo &b_ob_info, Hair *hair);
|
||||
void sync_hair_motion(BObjectInfo &b_ob_info, Hair *hair, int motion_step);
|
||||
void sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, const int motion_step = 0);
|
||||
void sync_particle_hair(Hair *hair,
|
||||
const blender::Mesh &b_mesh,
|
||||
BObjectInfo &b_ob_info,
|
||||
bool motion,
|
||||
const int motion_step = 0);
|
||||
bool object_has_particle_hair(blender::Object *b_ob);
|
||||
|
||||
/* Point Cloud */
|
||||
void sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info);
|
||||
void sync_pointcloud_motion(PointCloud *pointcloud,
|
||||
BObjectInfo &b_ob_info,
|
||||
const int motion_step = 0);
|
||||
|
||||
/* Camera */
|
||||
void sync_camera_motion(const blender::RenderData &b_render,
|
||||
blender::Object *b_ob,
|
||||
const int width,
|
||||
const int height,
|
||||
const float motion_time);
|
||||
|
||||
/* Geometry */
|
||||
Geometry *sync_geometry(BObjectInfo &b_ob_info,
|
||||
bool object_updated,
|
||||
bool use_particle_hair,
|
||||
TaskPool *task_pool);
|
||||
|
||||
void sync_geometry_motion(BObjectInfo &b_ob_info,
|
||||
Object *object,
|
||||
const float motion_time,
|
||||
bool use_particle_hair,
|
||||
TaskPool *task_pool);
|
||||
|
||||
/* Light */
|
||||
Geometry *create_light(BObjectInfo &b_ob_info);
|
||||
void sync_light(BObjectInfo &b_ob_info, Light *light);
|
||||
void sync_background_light(blender::bScreen *b_screen, blender::View3D *b_v3d);
|
||||
|
||||
/* Particles */
|
||||
bool sync_dupli_particle(blender::Object &b_parent,
|
||||
blender::DEGObjectIterData &b_deg_iter_data,
|
||||
blender::Object &b_ob,
|
||||
Object *object);
|
||||
|
||||
/* Images. */
|
||||
void sync_images();
|
||||
|
||||
/* util */
|
||||
void find_shader(const blender::ID *id, array<Node *> &used_shaders, Shader *default_shader);
|
||||
bool BKE_object_is_modified(blender::Object &b_ob);
|
||||
bool object_is_geometry(BObjectInfo &b_ob_info);
|
||||
bool object_can_have_geometry(blender::Object &b_ob);
|
||||
bool object_is_light(blender::Object &b_ob);
|
||||
bool object_is_camera(blender::Object &b_ob);
|
||||
|
||||
blender::Object *get_camera_object(blender::View3D *b_v3d, blender::RegionView3D *b_rv3d);
|
||||
blender::Object *get_dicing_camera_object(blender::View3D *b_v3d, blender::RegionView3D *b_rv3d);
|
||||
|
||||
/* variables */
|
||||
blender::RenderEngine *b_engine;
|
||||
blender::Main *b_data;
|
||||
blender::Scene *b_scene;
|
||||
blender::Object *b_bake_target;
|
||||
|
||||
enum ShaderFlags { SHADER_WITH_LAYER_ATTRS };
|
||||
|
||||
id_map<const void *, Shader, ShaderFlags> shader_map;
|
||||
/* To keep track of the AOVs in consecutive view layers that are rendered, this is the old data
|
||||
* for comparing. */
|
||||
blender::Vector<std::pair<std::string, int>> shader_view_layer_aovs;
|
||||
id_map<ObjectKey, Object> object_map;
|
||||
id_map<void *, Procedural> procedural_map;
|
||||
id_map<GeometryKey, Geometry> geometry_map;
|
||||
id_map<ParticleSystemKey, ParticleSystem> particle_system_map;
|
||||
set<Geometry *> geometry_synced;
|
||||
set<Geometry *> geometry_motion_synced;
|
||||
set<Geometry *> geometry_motion_attribute_synced;
|
||||
/** Remember which geometries come from which objects to be able to sync them after changes. */
|
||||
map<void *, set<blender::ID *>> instance_geometries_by_object;
|
||||
set<float> motion_times;
|
||||
void *world_map;
|
||||
bool world_recalc;
|
||||
BlenderViewportParameters viewport_parameters;
|
||||
|
||||
Scene *scene;
|
||||
bool preview;
|
||||
bool use_adaptive_subdivision = false;
|
||||
bool use_developer_ui;
|
||||
|
||||
CurveShapeType curve_shape = CURVE_RIBBON;
|
||||
|
||||
float dicing_rate;
|
||||
int max_subdivisions;
|
||||
|
||||
struct RenderLayerInfo {
|
||||
string name;
|
||||
blender::Material *material_override = nullptr;
|
||||
blender::World *world_override = nullptr;
|
||||
bool use_background_shader = true;
|
||||
bool use_surfaces = true;
|
||||
bool use_hair = true;
|
||||
bool use_volumes = true;
|
||||
bool use_motion_blur = true;
|
||||
int samples = 0;
|
||||
bool bound_samples = false;
|
||||
} view_layer;
|
||||
|
||||
Progress &progress;
|
||||
|
||||
/* Indicates that `sync_recalc()` detected changes in the scene.
|
||||
* If this flag is false then the data is considered to be up-to-date and will not be
|
||||
* synchronized at all. */
|
||||
bool has_updates_ = true;
|
||||
|
||||
float frame_last_synced = 0;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
59
blender-5.2.0/intern/cycles/blender/texture_cache.cpp
Normal file
59
blender-5.2.0/intern/cycles/blender/texture_cache.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "blender/CCL_api.h"
|
||||
|
||||
#include "util/image_maketx.h"
|
||||
#include "util/image_metadata.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool CCL_resolve_texture_cache(const Image *image,
|
||||
const char *filepath,
|
||||
const char *texture_cache_directory,
|
||||
std::string &r_tx_filepath)
|
||||
{
|
||||
ccl::ImageMetaData tx_metadata;
|
||||
return ccl::resolve_tx(filepath,
|
||||
texture_cache_directory,
|
||||
ccl::ustring(image->colorspace_settings.name),
|
||||
ccl::ImageAlphaType(image->alpha_mode),
|
||||
ccl::IMAGE_FORMAT_PLAIN,
|
||||
r_tx_filepath,
|
||||
tx_metadata);
|
||||
}
|
||||
|
||||
bool CCL_generate_texture_cache(const Image *image,
|
||||
const char *filepath,
|
||||
const char *texture_cache_directory)
|
||||
{
|
||||
std::string tx_filepath;
|
||||
ccl::ImageMetaData tx_metadata;
|
||||
const bool is_valid = ccl::resolve_tx(filepath,
|
||||
texture_cache_directory,
|
||||
ccl::ustring(image->colorspace_settings.name),
|
||||
ccl::ImageAlphaType(image->alpha_mode),
|
||||
ccl::IMAGE_FORMAT_PLAIN,
|
||||
tx_filepath,
|
||||
tx_metadata);
|
||||
|
||||
if (is_valid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tx_filepath.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return make_tx(filepath,
|
||||
tx_filepath,
|
||||
ccl::ustring(image->colorspace_settings.name),
|
||||
ccl::ImageAlphaType(image->alpha_mode),
|
||||
ccl::IMAGE_FORMAT_PLAIN);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
821
blender-5.2.0/intern/cycles/blender/util.h
Normal file
821
blender-5.2.0/intern/cycles/blender/util.h
Normal file
@@ -0,0 +1,821 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_colorband.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "DNA_fluid_types.h"
|
||||
#include "DNA_text_types.h"
|
||||
#include "RE_engine.h"
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/array.h"
|
||||
#include "util/path.h"
|
||||
#include "util/set.h"
|
||||
#include "util/transform.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_types.hh"
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* To make GS macro work. */
|
||||
using ID_Type = blender::ID_Type;
|
||||
|
||||
static inline blender::ID *object_get_data(const blender::Object &b_ob,
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
if (!use_adaptive_subdivision && b_ob.type == blender::OB_MESH) {
|
||||
return &BKE_mesh_wrapper_ensure_subdivision(blender::id_cast<blender::Mesh *>(b_ob.data))->id;
|
||||
}
|
||||
|
||||
return reinterpret_cast<blender::ID *>(b_ob.data);
|
||||
}
|
||||
|
||||
struct BObjectInfo {
|
||||
/* Object directly provided by the depsgraph iterator. This object is only valid during one
|
||||
* iteration and must not be accessed afterwards. Transforms and visibility should be checked on
|
||||
* this object. */
|
||||
blender::Object *iter_object;
|
||||
|
||||
/* This object remains alive even after the object iterator is done. It corresponds to one
|
||||
* original object. It is the object that owns the object data below. */
|
||||
blender::Object *real_object;
|
||||
|
||||
/* The object-data referenced by the iter object. This is still valid after the depsgraph
|
||||
* iterator is done. It might have a different type compared to object_get_data(real_object). */
|
||||
blender::ID *object_data;
|
||||
|
||||
/* Object will use adaptive subdivision. */
|
||||
bool use_adaptive_subdivision;
|
||||
|
||||
/* True when the current geometry is the data of the referenced object. False when it is a
|
||||
* geometry instance that does not have a 1-to-1 relationship with an object. */
|
||||
bool is_real_object_data() const
|
||||
{
|
||||
return object_get_data(*real_object, use_adaptive_subdivision) == object_data;
|
||||
}
|
||||
};
|
||||
|
||||
static inline blender::Mesh *object_copy_mesh_data(const BObjectInfo &b_ob_info)
|
||||
{
|
||||
blender::Mesh *mesh = BKE_mesh_new_from_object(
|
||||
nullptr, b_ob_info.real_object, false, false, !b_ob_info.use_adaptive_subdivision);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
int blender_attribute_name_split_type(ustring name, string *r_real_name);
|
||||
|
||||
void python_thread_state_save(void **python_thread_state);
|
||||
void python_thread_state_restore(void **python_thread_state);
|
||||
|
||||
static inline blender::Mesh *object_to_mesh(BObjectInfo &b_ob_info)
|
||||
{
|
||||
blender::Mesh *mesh = (GS(b_ob_info.object_data->name) == blender::ID_ME) ?
|
||||
blender::id_cast<blender::Mesh *>(b_ob_info.object_data) :
|
||||
nullptr;
|
||||
|
||||
if (b_ob_info.is_real_object_data()) {
|
||||
if (mesh) {
|
||||
if (mesh->runtime->edit_mesh) {
|
||||
/* Flush edit-mesh to mesh, including all data layers. */
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO: what to do about non-mesh geometry instances? */
|
||||
}
|
||||
|
||||
if (mesh) {
|
||||
if (b_ob_info.use_adaptive_subdivision) {
|
||||
mesh->corner_tris();
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static inline void free_object_to_mesh(BObjectInfo &b_ob_info, blender::Mesh &mesh)
|
||||
{
|
||||
if (!b_ob_info.is_real_object_data()) {
|
||||
return;
|
||||
}
|
||||
/* Free mesh if we didn't just use the existing one. */
|
||||
blender::Object *object = b_ob_info.real_object;
|
||||
if (object_get_data(*object, b_ob_info.use_adaptive_subdivision) != &mesh.id) {
|
||||
BKE_id_free(nullptr, &mesh.id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void colorramp_to_array(const blender::ColorBand &ramp,
|
||||
array<packed_float3> &ramp_color,
|
||||
array<float> &ramp_alpha,
|
||||
const int size)
|
||||
{
|
||||
const int full_size = size + 1;
|
||||
ramp_color.resize(full_size);
|
||||
ramp_alpha.resize(full_size);
|
||||
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
float color[4];
|
||||
|
||||
BKE_colorband_evaluate(&ramp, float(i) / float(size), color);
|
||||
ramp_color[i] = make_float3(color[0], color[1], color[2]);
|
||||
ramp_alpha[i] = color[3];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemap_minmax_curve(const blender::CurveMap &curve,
|
||||
float *min_x,
|
||||
float *max_x)
|
||||
{
|
||||
const blender::Span<blender::CurveMapPoint> points(curve.curve, curve.totpoint);
|
||||
*min_x = min(*min_x, points.first().x);
|
||||
*max_x = max(*max_x, points.last().x);
|
||||
}
|
||||
|
||||
static inline void curvemapping_minmax(const blender::CurveMapping &cumap,
|
||||
const int num_curves,
|
||||
float *min_x,
|
||||
float *max_x)
|
||||
{
|
||||
// const int num_curves = cumap.curves.length(); /* Gives linking error so far. */
|
||||
*min_x = FLT_MAX;
|
||||
*max_x = -FLT_MAX;
|
||||
for (int i = 0; i < num_curves; ++i) {
|
||||
const blender::CurveMap &map(cumap.cm[i]);
|
||||
curvemap_minmax_curve(map, min_x, max_x);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemapping_to_array(const blender::CurveMapping &cumap,
|
||||
array<float> &data,
|
||||
const int size)
|
||||
{
|
||||
BKE_curvemapping_changed_all(&const_cast<blender::CurveMapping &>(cumap));
|
||||
const blender::CurveMap &curve = cumap.cm[0];
|
||||
const int full_size = size + 1;
|
||||
data.resize(full_size);
|
||||
if (!curve.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = float(i) / float(size);
|
||||
data[i] = BKE_curvemap_evaluateF(&cumap, &curve, t);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemapping_float_to_array(const blender::CurveMapping &cumap,
|
||||
array<float> &data,
|
||||
const int size)
|
||||
{
|
||||
float min = 0.0f;
|
||||
float max = 1.0f;
|
||||
|
||||
curvemapping_minmax(cumap, 1, &min, &max);
|
||||
|
||||
const float range = max - min;
|
||||
|
||||
BKE_curvemapping_changed_all(&const_cast<blender::CurveMapping &>(cumap));
|
||||
|
||||
const blender::CurveMap &map = cumap.cm[0];
|
||||
|
||||
const int full_size = size + 1;
|
||||
data.resize(full_size);
|
||||
if (!map.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = min + float(i) / float(size) * range;
|
||||
data[i] = BKE_curvemap_evaluateF(&cumap, &map, t);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemapping_color_to_array(const blender::CurveMapping &cumap,
|
||||
array<packed_float3> &data,
|
||||
const int size,
|
||||
bool rgb_curve)
|
||||
{
|
||||
float min_x = 0.0f;
|
||||
float max_x = 1.0f;
|
||||
|
||||
/* TODO(sergey): There is no easy way to automatically guess what is
|
||||
* the range to be used here for the case when mapping is applied on
|
||||
* top of another mapping (i.e. R curve applied on top of common
|
||||
* one).
|
||||
*
|
||||
* Using largest possible range form all curves works correct for the
|
||||
* cases like vector curves and should be good enough heuristic for
|
||||
* the color curves as well.
|
||||
*
|
||||
* There might be some better estimations here tho.
|
||||
*/
|
||||
const int num_curves = rgb_curve ? 4 : 3;
|
||||
curvemapping_minmax(cumap, num_curves, &min_x, &max_x);
|
||||
|
||||
const float range_x = max_x - min_x;
|
||||
|
||||
BKE_curvemapping_changed_all(&const_cast<blender::CurveMapping &>(cumap));
|
||||
|
||||
const blender::CurveMap &mapR = cumap.cm[0];
|
||||
const blender::CurveMap &mapG = cumap.cm[1];
|
||||
const blender::CurveMap &mapB = cumap.cm[2];
|
||||
if (!mapR.table || !mapG.table || !mapB.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
|
||||
const int full_size = size + 1;
|
||||
data.resize(full_size);
|
||||
|
||||
if (rgb_curve) {
|
||||
const blender::CurveMap &mapI = cumap.cm[3];
|
||||
if (!mapR.table || !mapG.table || !mapB.table || !mapI.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = min_x + float(i) / float(size) * range_x;
|
||||
data[i] = make_float3(
|
||||
BKE_curvemap_evaluateF(&cumap, &mapR, BKE_curvemap_evaluateF(&cumap, &mapI, t)),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapG, BKE_curvemap_evaluateF(&cumap, &mapI, t)),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapB, BKE_curvemap_evaluateF(&cumap, &mapI, t)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!mapR.table || !mapG.table || !mapB.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = min_x + float(i) / float(size) * range_x;
|
||||
data[i] = make_float3(BKE_curvemap_evaluateF(&cumap, &mapR, t),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapG, t),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapB, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool BKE_object_is_deform_modified(BObjectInfo &self,
|
||||
blender::Scene &scene,
|
||||
bool preview)
|
||||
{
|
||||
if (!self.is_real_object_data()) {
|
||||
/* Comes from geometry nodes, can't use heuristic to guess if it's animated. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Use heuristic to quickly check if object is potentially animated. */
|
||||
const int settings = preview ? blender::eModifierMode_Realtime : blender::eModifierMode_Render;
|
||||
return (blender::BKE_object_is_deform_modified(&scene, self.real_object) & settings) != 0;
|
||||
}
|
||||
|
||||
static inline int render_resolution_x(const blender::RenderData &b_render)
|
||||
{
|
||||
return b_render.xsch * b_render.size / 100;
|
||||
}
|
||||
|
||||
static inline int render_resolution_y(const blender::RenderData &b_render)
|
||||
{
|
||||
return b_render.ysch * b_render.size / 100;
|
||||
}
|
||||
|
||||
static inline string image_user_file_path(blender::Main &data,
|
||||
blender::ImageUser &iuser,
|
||||
blender::Image &ima,
|
||||
const int cfra)
|
||||
{
|
||||
char filepath[1024];
|
||||
BKE_image_user_frame_calc(&ima, &iuser, cfra);
|
||||
BKE_image_user_file_path_ex(&data, &iuser, &ima, filepath, false, true);
|
||||
|
||||
return string(filepath);
|
||||
}
|
||||
|
||||
static inline int image_user_frame_number(blender::ImageUser &iuser,
|
||||
blender::Image &ima,
|
||||
const int cfra)
|
||||
{
|
||||
BKE_image_user_frame_calc(&ima, &iuser, cfra);
|
||||
return iuser.framenr;
|
||||
}
|
||||
|
||||
static inline bool image_is_builtin(blender::Image &ima, blender::RenderEngine &engine)
|
||||
{
|
||||
const blender::eImageSource image_source = blender::eImageSource(ima.source);
|
||||
if (image_source == blender::IMA_SRC_TILED) {
|
||||
/* If any tile is marked as generated, then treat the entire Image as built-in. */
|
||||
for (blender::ImageTile &tile : ima.tiles) {
|
||||
if (tile.gen_flag & blender::IMA_GEN_TILE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BKE_image_has_packedfile(&ima) || image_source == blender::IMA_SRC_GENERATED ||
|
||||
image_source == blender::IMA_SRC_MOVIE || BKE_image_is_dirty(&ima) ||
|
||||
((engine.flag & blender::RE_ENGINE_PREVIEW) != 0 &&
|
||||
image_source != blender::IMA_SRC_SEQUENCE);
|
||||
}
|
||||
|
||||
static inline void render_add_metadata(blender::RenderResult &b_rr, string name, string value)
|
||||
{
|
||||
BKE_render_result_stamp_data(&b_rr, name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
static inline Transform get_transform(const blender::float4x4 &matrix)
|
||||
{
|
||||
/* Convert from Blender column major to Cycles row major, assume it's an affine transform that
|
||||
* does not need the last row. */
|
||||
const float *ptr = matrix.base_ptr();
|
||||
return make_transform(ptr[0],
|
||||
ptr[4],
|
||||
ptr[8],
|
||||
ptr[12],
|
||||
|
||||
ptr[1],
|
||||
ptr[5],
|
||||
ptr[9],
|
||||
ptr[13],
|
||||
|
||||
ptr[2],
|
||||
ptr[6],
|
||||
ptr[10],
|
||||
ptr[14]);
|
||||
}
|
||||
|
||||
static inline float2 get_float2(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
float2 f;
|
||||
RNA_float_get_array(&ptr, name, &f.x);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline void set_float2(blender::PointerRNA &ptr, const char *name, const float2 value)
|
||||
{
|
||||
RNA_float_set_array(&ptr, name, &value.x);
|
||||
}
|
||||
|
||||
static inline float3 get_float3(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
float3 f;
|
||||
RNA_float_get_array(&ptr, name, &f.x);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline void set_float3(blender::PointerRNA &ptr, const char *name, const float3 value)
|
||||
{
|
||||
RNA_float_set_array(&ptr, name, &value.x);
|
||||
}
|
||||
|
||||
static inline float4 get_float4(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
float4 f;
|
||||
RNA_float_get_array(&ptr, name, &f.x);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline void set_float4(blender::PointerRNA &ptr, const char *name, const float4 value)
|
||||
{
|
||||
RNA_float_set_array(&ptr, name, &value.x);
|
||||
}
|
||||
|
||||
static inline bool get_boolean(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_boolean_get(&ptr, name) ? true : false;
|
||||
}
|
||||
|
||||
static inline void set_boolean(blender::PointerRNA &ptr, const char *name, bool value)
|
||||
{
|
||||
RNA_boolean_set(&ptr, name, (int)value);
|
||||
}
|
||||
|
||||
static inline float get_float(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_float_get(&ptr, name);
|
||||
}
|
||||
|
||||
static inline void set_float(blender::PointerRNA &ptr, const char *name, const float value)
|
||||
{
|
||||
RNA_float_set(&ptr, name, value);
|
||||
}
|
||||
|
||||
static inline int get_int(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_int_get(&ptr, name);
|
||||
}
|
||||
|
||||
static inline void set_int(blender::PointerRNA &ptr, const char *name, const int value)
|
||||
{
|
||||
RNA_int_set(&ptr, name, value);
|
||||
}
|
||||
|
||||
/* Get a RNA enum value with sanity check: if the RNA value is above num_values
|
||||
* the function will return a fallback default value.
|
||||
*
|
||||
* NOTE: This function assumes that RNA enum values are a continuous sequence
|
||||
* from 0 to num_values-1. Be careful to use it with enums where some values are
|
||||
* deprecated!
|
||||
*/
|
||||
static inline int get_enum(blender::PointerRNA &ptr,
|
||||
const char *name,
|
||||
int num_values = -1,
|
||||
int default_value = -1)
|
||||
{
|
||||
int value = RNA_enum_get(&ptr, name);
|
||||
if (num_values != -1 && value >= num_values) {
|
||||
assert(default_value != -1);
|
||||
value = default_value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline string get_enum_identifier(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
|
||||
const char *identifier = "";
|
||||
const int value = RNA_property_enum_get(&ptr, prop);
|
||||
|
||||
RNA_property_enum_identifier(nullptr, &ptr, prop, value, &identifier);
|
||||
|
||||
return string(identifier);
|
||||
}
|
||||
|
||||
static inline void set_enum(blender::PointerRNA &ptr, const char *name, const int value)
|
||||
{
|
||||
RNA_enum_set(&ptr, name, value);
|
||||
}
|
||||
|
||||
static inline void set_enum(blender::PointerRNA &ptr, const char *name, const string &identifier)
|
||||
{
|
||||
RNA_enum_set_identifier(nullptr, &ptr, name, identifier.c_str());
|
||||
}
|
||||
|
||||
static inline string get_string(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_string_get(&ptr, name);
|
||||
}
|
||||
|
||||
static inline void set_string(blender::PointerRNA &ptr, const char *name, const string &value)
|
||||
{
|
||||
RNA_string_set(&ptr, name, value.c_str());
|
||||
}
|
||||
|
||||
/* Relative Paths */
|
||||
|
||||
static inline string blender_absolute_path(blender::Main &b_data,
|
||||
blender::ID *b_id,
|
||||
const string &path)
|
||||
{
|
||||
if (path.size() >= 2 && path[0] == '/' && path[1] == '/') {
|
||||
string dirname;
|
||||
|
||||
if (b_id && b_id->lib) {
|
||||
dirname = blender_absolute_path(b_data, &b_id->lib->id, b_id->lib->filepath);
|
||||
}
|
||||
else {
|
||||
dirname = b_data.filepath;
|
||||
}
|
||||
|
||||
return path_join(path_dirname(dirname), path.substr(2));
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static inline string get_text_datablock_content(const blender::ID *id)
|
||||
{
|
||||
if (id == nullptr) {
|
||||
return "";
|
||||
}
|
||||
if (GS(id->name) != blender::ID_TXT) {
|
||||
return "";
|
||||
}
|
||||
const auto &text = *blender::id_cast<const blender::Text *>(id);
|
||||
|
||||
string content;
|
||||
for (blender::TextLine &line : text.lines) {
|
||||
content += line.line ? line.line : "";
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/* Texture Space */
|
||||
|
||||
static inline void mesh_texture_space(const blender::Mesh &b_mesh, float3 &loc, float3 &size)
|
||||
{
|
||||
float texspace_location[3];
|
||||
float texspace_size[3];
|
||||
BKE_mesh_texspace_get(const_cast<blender::Mesh *>(&b_mesh), texspace_location, texspace_size);
|
||||
|
||||
loc = make_float3(texspace_location[0], texspace_location[1], texspace_location[2]);
|
||||
size = make_float3(texspace_size[0], texspace_size[1], texspace_size[2]);
|
||||
|
||||
if (size.x != 0.0f) {
|
||||
size.x = 0.5f / size.x;
|
||||
}
|
||||
if (size.y != 0.0f) {
|
||||
size.y = 0.5f / size.y;
|
||||
}
|
||||
if (size.z != 0.0f) {
|
||||
size.z = 0.5f / size.z;
|
||||
}
|
||||
|
||||
loc = loc * size - make_float3(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
|
||||
/* Object motion steps, returns 0 if no motion blur needed. */
|
||||
static inline uint object_motion_steps(blender::Object &b_parent,
|
||||
blender::Object &b_ob,
|
||||
const int max_steps = INT_MAX)
|
||||
{
|
||||
/* Get motion enabled and steps from object itself. */
|
||||
blender::PointerRNA object_rna_ptr = RNA_id_pointer_create(&b_ob.id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&object_rna_ptr, "cycles");
|
||||
bool use_motion = get_boolean(cobject, "use_motion_blur");
|
||||
if (!use_motion) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int steps = max(1, get_int(cobject, "motion_steps"));
|
||||
|
||||
/* Also check parent object, so motion blur and steps can be
|
||||
* controlled by dupli-group duplicator for linked groups. */
|
||||
if (&b_parent != &b_ob) {
|
||||
blender::PointerRNA parent_rna_ptr = RNA_id_pointer_create(&b_parent.id);
|
||||
blender::PointerRNA parent_cobject = RNA_pointer_get(&parent_rna_ptr, "cycles");
|
||||
use_motion &= get_boolean(parent_cobject, "use_motion_blur");
|
||||
|
||||
if (!use_motion) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
steps = max(steps, get_int(parent_cobject, "motion_steps"));
|
||||
}
|
||||
|
||||
/* Use uneven number of steps so we get one keyframe at the current frame,
|
||||
* and use 2^(steps - 1) so objects with more/fewer steps still have samples
|
||||
* at the same times, to avoid sampling at many different times. */
|
||||
return min((2 << (steps - 1)) + 1, max_steps);
|
||||
}
|
||||
|
||||
/* object uses deformation motion blur */
|
||||
static inline bool object_use_deform_motion(blender::Object &b_parent, blender::Object &b_ob)
|
||||
{
|
||||
blender::PointerRNA b_ob_rna_ptr = RNA_id_pointer_create(&b_ob.id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&b_ob_rna_ptr, "cycles");
|
||||
bool use_deform_motion = get_boolean(cobject, "use_deform_motion");
|
||||
/* If motion blur is enabled for the object we also check
|
||||
* whether it's enabled for the parent object as well.
|
||||
*
|
||||
* This way we can control motion blur from the dupli-group
|
||||
* duplicator much easier. */
|
||||
if (use_deform_motion && &b_parent != &b_ob) {
|
||||
blender::PointerRNA b_parent_rna_ptr = RNA_id_pointer_create(&b_parent.id);
|
||||
blender::PointerRNA parent_cobject = RNA_pointer_get(&b_parent_rna_ptr, "cycles");
|
||||
use_deform_motion &= get_boolean(parent_cobject, "use_deform_motion");
|
||||
}
|
||||
return use_deform_motion;
|
||||
}
|
||||
|
||||
static inline blender::FluidDomainSettings *object_fluid_gas_domain_find(blender::Object &b_ob)
|
||||
{
|
||||
for (blender::ModifierData &b_mod : b_ob.modifiers) {
|
||||
if (b_mod.type == blender::eModifierType_Fluid) {
|
||||
auto *b_mmd = reinterpret_cast<blender::FluidModifierData *>(&b_mod);
|
||||
|
||||
if (b_mmd->type == blender::MOD_FLUID_TYPE_DOMAIN &&
|
||||
b_mmd->domain->type == blender::FLUID_DOMAIN_TYPE_GAS)
|
||||
{
|
||||
return b_mmd->domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static blender::SubsurfModifierData *object_subdivision_modifier(blender::Object &b_ob,
|
||||
const bool preview)
|
||||
{
|
||||
blender::ModifierData *md = static_cast<blender::ModifierData *>(b_ob.modifiers.last);
|
||||
if (!md) {
|
||||
return nullptr;
|
||||
}
|
||||
if (md->type != blender::eModifierType_Subsurf) {
|
||||
return nullptr;
|
||||
}
|
||||
const blender::ModifierMode enabled_mode = preview ? blender::eModifierMode_Realtime :
|
||||
blender::eModifierMode_Render;
|
||||
if ((md->mode & enabled_mode) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
blender::SubsurfModifierData *subsurf = reinterpret_cast<blender::SubsurfModifierData *>(md);
|
||||
if ((subsurf->flags & blender::eSubsurfModifierFlag_UseAdaptiveSubdivision) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return subsurf;
|
||||
}
|
||||
|
||||
static inline Mesh::SubdivisionType object_subdivision_type(blender::Object &b_ob,
|
||||
const bool preview,
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
if (!use_adaptive_subdivision) {
|
||||
return Mesh::SUBDIVISION_NONE;
|
||||
}
|
||||
|
||||
blender::SubsurfModifierData *subsurf = object_subdivision_modifier(b_ob, preview);
|
||||
|
||||
if (subsurf) {
|
||||
if (subsurf->subdivType == blender::SUBSURF_TYPE_CATMULL_CLARK) {
|
||||
return Mesh::SUBDIVISION_CATMULL_CLARK;
|
||||
}
|
||||
return Mesh::SUBDIVISION_LINEAR;
|
||||
}
|
||||
|
||||
return Mesh::SUBDIVISION_NONE;
|
||||
}
|
||||
|
||||
static inline void object_subdivision_to_mesh(blender::Object &b_ob,
|
||||
Mesh &mesh,
|
||||
const bool preview,
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
if (!use_adaptive_subdivision) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
blender::SubsurfModifierData *subsurf = object_subdivision_modifier(b_ob, preview);
|
||||
|
||||
if (!subsurf) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subsurf->subdivType != blender::SUBSURF_TYPE_CATMULL_CLARK) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
|
||||
return;
|
||||
}
|
||||
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
|
||||
|
||||
switch (subsurf->boundary_smooth) {
|
||||
case blender::SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS:
|
||||
mesh.set_subdivision_boundary_interpolation(Mesh::SUBDIVISION_BOUNDARY_EDGE_AND_CORNER);
|
||||
break;
|
||||
case blender::SUBSURF_BOUNDARY_SMOOTH_ALL:
|
||||
mesh.set_subdivision_boundary_interpolation(Mesh::SUBDIVISION_BOUNDARY_EDGE_ONLY);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (subsurf->uv_smooth) {
|
||||
case blender::SUBSURF_UV_SMOOTH_NONE:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_ALL);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_CORNERS:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_ONLY);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS1);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS2);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_BOUNDARIES);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_ALL:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline PathRayVisibility object_ray_visibility(blender::Object &b_ob)
|
||||
{
|
||||
PathRayVisibility visibility = PATH_RAY_VISIBILITY_NONE;
|
||||
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_CAMERA) == 0) ?
|
||||
PATH_RAY_VISIBILITY_CAMERA :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_DIFFUSE) == 0) ?
|
||||
PATH_RAY_VISIBILITY_DIFFUSE :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_GLOSSY) == 0) ?
|
||||
PATH_RAY_VISIBILITY_GLOSSY :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_TRANSMISSION) == 0) ?
|
||||
PATH_RAY_VISIBILITY_TRANSMIT :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_SHADOW) == 0) ?
|
||||
PATH_RAY_VISIBILITY_SHADOW :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_VOLUME_SCATTER) == 0) ?
|
||||
PATH_RAY_VISIBILITY_VOLUME_SCATTER :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
|
||||
return visibility;
|
||||
}
|
||||
|
||||
/* Check whether some of "built-in" motion-related attributes are needed to be exported (includes
|
||||
* things like velocity from cache modifier, fluid simulation).
|
||||
*
|
||||
* NOTE: This code is run prior to object motion blur initialization. so can not access properties
|
||||
* set by `sync_object_motion_init()`. */
|
||||
static inline bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
|
||||
{
|
||||
const Scene::MotionType need_motion = scene->need_motion();
|
||||
if (need_motion == Scene::MOTION_NONE) {
|
||||
/* Simple case: neither motion pass nor motion blur is needed, no need in the motion related
|
||||
* attributes. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (need_motion == Scene::MOTION_BLUR) {
|
||||
/* A bit tricky and implicit case:
|
||||
* - Motion blur is enabled in the scene, which implies specific number of time steps for
|
||||
* objects.
|
||||
* - If the object has motion blur disabled on it, it will have 0 time steps.
|
||||
* - Motion attribute expects non-zero time steps.
|
||||
*
|
||||
* Avoid adding motion attributes if the motion blur will enforce 0 motion steps. */
|
||||
blender::PointerRNA b_ob_rna_ptr = RNA_id_pointer_create(&b_ob_info.real_object->id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&b_ob_rna_ptr, "cycles");
|
||||
const bool use_motion = get_boolean(cobject, "use_motion_blur");
|
||||
if (!use_motion) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Motion pass which implies 3 motion steps, or motion blur which is not disabled on object
|
||||
* level. */
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool region_view3d_navigating_or_transforming(const blender::RegionView3D *b_rv3d)
|
||||
{
|
||||
return b_rv3d && ((b_rv3d->rflag & (blender::RV3D_NAVIGATING | blender::RV3D_PAINTING)) ||
|
||||
(blender::G.moving & (blender::G_TRANSFORM_OBJ | blender::G_TRANSFORM_EDIT)));
|
||||
}
|
||||
|
||||
class EdgeMap {
|
||||
public:
|
||||
EdgeMap() = default;
|
||||
|
||||
void clear()
|
||||
{
|
||||
edges_.clear();
|
||||
}
|
||||
|
||||
void insert(int v0, int v1)
|
||||
{
|
||||
get_sorted_verts(v0, v1);
|
||||
edges_.insert(std::pair<int, int>(v0, v1));
|
||||
}
|
||||
|
||||
bool exists(int v0, int v1)
|
||||
{
|
||||
get_sorted_verts(v0, v1);
|
||||
return edges_.contains(std::pair<int, int>(v0, v1));
|
||||
}
|
||||
|
||||
protected:
|
||||
void get_sorted_verts(int &v0, int &v1)
|
||||
{
|
||||
if (v0 > v1) {
|
||||
swap(v0, v1);
|
||||
}
|
||||
}
|
||||
|
||||
set<std::pair<int, int>> edges_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
105
blender-5.2.0/intern/cycles/blender/viewport.cpp
Normal file
105
blender-5.2.0/intern/cycles/blender/viewport.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "blender/viewport.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "scene/pass.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BlenderViewportParameters::BlenderViewportParameters()
|
||||
: use_scene_world(true),
|
||||
use_scene_lights(true),
|
||||
studiolight_rotate_z(0.0f),
|
||||
studiolight_intensity(1.0f),
|
||||
studiolight_background_alpha(1.0f),
|
||||
display_pass(PASS_COMBINED),
|
||||
show_active_pixels(false)
|
||||
{
|
||||
}
|
||||
|
||||
BlenderViewportParameters::BlenderViewportParameters(blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
bool use_developer_ui)
|
||||
: BlenderViewportParameters()
|
||||
{
|
||||
if (!b_v3d) {
|
||||
return;
|
||||
}
|
||||
|
||||
blender::View3DShading shading = b_v3d->shading;
|
||||
blender::PointerRNA v3d_rna_ptr = RNA_pointer_create_discrete(
|
||||
&b_screen->id, blender::RNA_SpaceView3D, b_v3d);
|
||||
blender::PointerRNA shading_rna_ptr = RNA_pointer_get(&v3d_rna_ptr, "shading");
|
||||
|
||||
/* We only copy the shading parameters if we are in look-dev mode.
|
||||
* Otherwise defaults are being used. These defaults mimic normal render settings. */
|
||||
if (shading.type == blender::OB_RENDER) {
|
||||
use_scene_world = shading.flag & blender::V3D_SHADING_SCENE_WORLD_RENDER;
|
||||
use_scene_lights = shading.flag & blender::V3D_SHADING_SCENE_LIGHTS_RENDER;
|
||||
|
||||
if (!use_scene_world) {
|
||||
studiolight_rotate_z = shading.studiolight_rot_z;
|
||||
studiolight_intensity = shading.studiolight_intensity;
|
||||
studiolight_background_alpha = shading.studiolight_background;
|
||||
blender::PointerRNA selected_studiolight_rna_ptr = RNA_pointer_get(&shading_rna_ptr,
|
||||
"selected_studio_light");
|
||||
studiolight_path = RNA_string_get(&selected_studiolight_rna_ptr, "path");
|
||||
}
|
||||
}
|
||||
|
||||
/* Film. */
|
||||
|
||||
/* Lookup display pass based on the enum identifier.
|
||||
* This is because integer values of python enum are not aligned with the passes definition in
|
||||
* the kernel. */
|
||||
|
||||
display_pass = PASS_COMBINED;
|
||||
|
||||
blender::PointerRNA cycles_shading_ptr = RNA_pointer_get(&shading_rna_ptr, "cycles");
|
||||
const string display_pass_identifier = get_enum_identifier(cycles_shading_ptr, "render_pass");
|
||||
if (!display_pass_identifier.empty()) {
|
||||
const ustring pass_type_identifier(string_to_lower(display_pass_identifier));
|
||||
const NodeEnum *pass_type_enum = Pass::get_type_enum();
|
||||
if (pass_type_enum->exists(pass_type_identifier)) {
|
||||
display_pass = static_cast<PassType>((*pass_type_enum)[pass_type_identifier]);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_developer_ui) {
|
||||
show_active_pixels = get_boolean(cycles_shading_ptr, "show_active_pixels");
|
||||
}
|
||||
}
|
||||
|
||||
bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const
|
||||
{
|
||||
return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights ||
|
||||
studiolight_rotate_z != other.studiolight_rotate_z ||
|
||||
studiolight_intensity != other.studiolight_intensity ||
|
||||
studiolight_background_alpha != other.studiolight_background_alpha ||
|
||||
studiolight_path != other.studiolight_path;
|
||||
}
|
||||
|
||||
bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const
|
||||
{
|
||||
return display_pass != other.display_pass || show_active_pixels != other.show_active_pixels;
|
||||
}
|
||||
|
||||
bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const
|
||||
{
|
||||
return shader_modified(other) || film_modified(other);
|
||||
}
|
||||
|
||||
bool BlenderViewportParameters::use_custom_shader() const
|
||||
{
|
||||
return !(use_scene_world && use_scene_lights);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
51
blender-5.2.0/intern/cycles/blender/viewport.h
Normal file
51
blender-5.2.0/intern/cycles/blender/viewport.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "util/param.h"
|
||||
|
||||
namespace blender {
|
||||
struct View3D;
|
||||
struct bScreen;
|
||||
} // namespace blender
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BlenderViewportParameters {
|
||||
public:
|
||||
/* Shader. */
|
||||
bool use_scene_world;
|
||||
bool use_scene_lights;
|
||||
float studiolight_rotate_z;
|
||||
float studiolight_intensity;
|
||||
float studiolight_background_alpha;
|
||||
ustring studiolight_path;
|
||||
|
||||
/* Film. */
|
||||
PassType display_pass;
|
||||
bool show_active_pixels;
|
||||
|
||||
BlenderViewportParameters();
|
||||
BlenderViewportParameters(blender::bScreen *b_screen,
|
||||
blender::View3D *b_v3d,
|
||||
bool use_developer_ui);
|
||||
|
||||
/* Check whether any of shading related settings are different from the given parameters. */
|
||||
bool shader_modified(const BlenderViewportParameters &other) const;
|
||||
|
||||
/* Check whether any of film related settings are different from the given parameters. */
|
||||
bool film_modified(const BlenderViewportParameters &other) const;
|
||||
|
||||
/* Check whether any of settings are different from the given parameters. */
|
||||
bool modified(const BlenderViewportParameters &other) const;
|
||||
|
||||
/* Returns truth when a custom shader defined by the viewport is to be used instead of the
|
||||
* regular background shader or scene light. */
|
||||
bool use_custom_shader() const;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
449
blender-5.2.0/intern/cycles/blender/volume.cpp
Normal file
449
blender-5.2.0/intern/cycles/blender/volume.cpp
Normal file
@@ -0,0 +1,449 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/volume.h"
|
||||
#include "scene/image.h"
|
||||
#include "scene/image_vdb.h"
|
||||
#include "scene/object.h"
|
||||
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
|
||||
#include "BKE_volume.hh"
|
||||
#include "BKE_volume_grid.hh"
|
||||
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* TODO: verify this is not loading unnecessary attributes. */
|
||||
class BlenderSmokeLoader : public VDBImageLoader {
|
||||
public:
|
||||
BlenderSmokeLoader(blender::Object &b_ob, AttributeStandard attribute, const float clipping)
|
||||
: VDBImageLoader(Attribute::standard_name(attribute), clipping),
|
||||
b_domain(object_fluid_gas_domain_find(b_ob)),
|
||||
attribute(attribute)
|
||||
{
|
||||
domain_rna_ptr = RNA_pointer_create_discrete(
|
||||
&b_ob.id, blender::RNA_FluidDomainSettings, b_domain);
|
||||
mesh_texture_space(
|
||||
*blender::id_cast<const blender::Mesh *>(b_ob.data), texspace_loc, texspace_size);
|
||||
}
|
||||
|
||||
void load_grid() override
|
||||
{
|
||||
if (!b_domain) {
|
||||
return;
|
||||
}
|
||||
|
||||
int channels;
|
||||
if (attribute == ATTR_STD_VOLUME_DENSITY || attribute == ATTR_STD_VOLUME_FLAME ||
|
||||
attribute == ATTR_STD_VOLUME_HEAT || attribute == ATTR_STD_VOLUME_TEMPERATURE)
|
||||
{
|
||||
channels = 1;
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_COLOR) {
|
||||
channels = 4;
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
|
||||
channels = 3;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
const int3 resolution = make_int3(b_domain->res[0], b_domain->res[1], b_domain->res[2]);
|
||||
int amplify = (b_domain->flags & blender::FLUID_DOMAIN_USE_NOISE) != 0 ?
|
||||
b_domain->noise_scale :
|
||||
1;
|
||||
|
||||
/* Velocity and heat data is always low-resolution. */
|
||||
if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
|
||||
amplify = 1;
|
||||
}
|
||||
|
||||
const size_t width = resolution.x * amplify;
|
||||
const size_t height = resolution.y * amplify;
|
||||
const size_t depth = resolution.z * amplify;
|
||||
|
||||
/* Create a matrix to transform from object space to mesh texture space.
|
||||
* This does not work with deformations but that can probably only be done
|
||||
* well with a volume grid mapping of coordinates.
|
||||
*
|
||||
* Mantaflow stores values at cell centers, while OpenVDB convention is
|
||||
* at corners. Offset by half a voxel to compensate for that. */
|
||||
const float3 half_voxel = make_float3(0.5f / width, 0.5f / height, 0.5f / depth);
|
||||
Transform transform_3d = transform_translate(-texspace_loc - half_voxel) *
|
||||
transform_scale(texspace_size);
|
||||
|
||||
vector<float> voxels;
|
||||
if (!get_voxels(width, height, depth, channels, voxels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid_from_dense_voxels(width, height, depth, channels, voxels.data(), transform_3d);
|
||||
}
|
||||
|
||||
bool get_voxels(const size_t width,
|
||||
const size_t height,
|
||||
const size_t depth,
|
||||
const int channels,
|
||||
vector<float> &voxels)
|
||||
{
|
||||
if (!b_domain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef WITH_FLUID
|
||||
voxels.resize(width * height * depth * channels);
|
||||
|
||||
if (attribute == ATTR_STD_VOLUME_DENSITY) {
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&domain_rna_ptr, "density_grid");
|
||||
if (RNA_property_array_length(&domain_rna_ptr, prop) == voxels.size()) {
|
||||
RNA_property_float_get_array(&domain_rna_ptr, prop, voxels.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_FLAME) {
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&domain_rna_ptr, "flame_grid");
|
||||
/* this is in range 0..1, and interpreted by the OpenGL smoke viewer
|
||||
* as 1500..3000 K with the first part faded to zero density */
|
||||
if (RNA_property_array_length(&domain_rna_ptr, prop) == voxels.size()) {
|
||||
RNA_property_float_get_array(&domain_rna_ptr, prop, voxels.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_COLOR) {
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&domain_rna_ptr, "color_grid");
|
||||
/* the RGB is "premultiplied" by density for better interpolation results */
|
||||
if (RNA_property_array_length(&domain_rna_ptr, prop) == voxels.size()) {
|
||||
RNA_property_float_get_array(&domain_rna_ptr, prop, voxels.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&domain_rna_ptr, "velocity_grid");
|
||||
if (RNA_property_array_length(&domain_rna_ptr, prop) == voxels.size()) {
|
||||
RNA_property_float_get_array(&domain_rna_ptr, prop, voxels.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_HEAT) {
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&domain_rna_ptr, "heat_grid");
|
||||
if (RNA_property_array_length(&domain_rna_ptr, prop) == voxels.size()) {
|
||||
RNA_property_float_get_array(&domain_rna_ptr, prop, voxels.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (attribute == ATTR_STD_VOLUME_TEMPERATURE) {
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&domain_rna_ptr, "temperature_grid");
|
||||
if (RNA_property_array_length(&domain_rna_ptr, prop) == voxels.size()) {
|
||||
RNA_property_float_get_array(&domain_rna_ptr, prop, voxels.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR << "Unknown volume attribute " << Attribute::standard_name(attribute)
|
||||
<< "skipping ";
|
||||
voxels[0] = 0.0f;
|
||||
return false;
|
||||
}
|
||||
LOG_ERROR << "Unexpected smoke volume resolution, skipping";
|
||||
#else
|
||||
(void)voxels;
|
||||
(void)width;
|
||||
(void)height;
|
||||
(void)depth;
|
||||
(void)channels;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
string name() const override
|
||||
{
|
||||
return Attribute::standard_name(attribute);
|
||||
}
|
||||
|
||||
bool equals(const ImageLoader &other) const override
|
||||
{
|
||||
const BlenderSmokeLoader &other_loader = (const BlenderSmokeLoader &)other;
|
||||
return b_domain == other_loader.b_domain && attribute == other_loader.attribute;
|
||||
}
|
||||
|
||||
blender::FluidDomainSettings *b_domain;
|
||||
blender::PointerRNA domain_rna_ptr;
|
||||
float3 texspace_loc, texspace_size;
|
||||
AttributeStandard attribute;
|
||||
};
|
||||
|
||||
static void sync_smoke_volume(blender::Scene &b_scene,
|
||||
Scene *scene,
|
||||
BObjectInfo &b_ob_info,
|
||||
Volume *volume,
|
||||
const float frame)
|
||||
{
|
||||
if (!b_ob_info.is_real_object_data()) {
|
||||
return;
|
||||
}
|
||||
blender::FluidDomainSettings *b_domain = object_fluid_gas_domain_find(*b_ob_info.real_object);
|
||||
if (!b_domain) {
|
||||
return;
|
||||
}
|
||||
|
||||
float velocity_scale = b_domain->velocity_scale;
|
||||
/* Motion blur attribute is relative to seconds, we need it relative to frames. */
|
||||
const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
|
||||
const float motion_scale = (need_motion) ? scene->motion_shutter_time() /
|
||||
(b_scene.r.frs_sec / b_scene.r.frs_sec_base) :
|
||||
0.0f;
|
||||
|
||||
velocity_scale *= motion_scale;
|
||||
|
||||
volume->set_velocity_scale(velocity_scale);
|
||||
|
||||
const AttributeStandard attributes[] = {ATTR_STD_VOLUME_DENSITY,
|
||||
ATTR_STD_VOLUME_COLOR,
|
||||
ATTR_STD_VOLUME_FLAME,
|
||||
ATTR_STD_VOLUME_HEAT,
|
||||
ATTR_STD_VOLUME_TEMPERATURE,
|
||||
ATTR_STD_VOLUME_VELOCITY,
|
||||
ATTR_STD_NONE};
|
||||
|
||||
const Interval<int> frame_interval = {b_domain->cache_frame_start, b_domain->cache_frame_end};
|
||||
|
||||
for (int i = 0; attributes[i] != ATTR_STD_NONE; i++) {
|
||||
const AttributeStandard std = attributes[i];
|
||||
if (!volume->need_attribute(scene, std)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float clipping = b_domain->clipping;
|
||||
|
||||
Attribute *attr = volume->attributes.add(std);
|
||||
|
||||
if (!frame_interval.contains(frame)) {
|
||||
attr->data_voxel_for_write().clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
unique_ptr<ImageLoader> loader = make_unique<BlenderSmokeLoader>(
|
||||
*b_ob_info.real_object, std, clipping);
|
||||
ImageParams params;
|
||||
params.frame = frame;
|
||||
|
||||
attr->data_voxel_for_write() = scene->image_manager->add_image(std::move(loader), params);
|
||||
}
|
||||
|
||||
/* Create a matrix to transform from object space to normalized texture space [0, 1]. */
|
||||
if (volume->need_attribute(scene, ATTR_STD_GENERATED_TRANSFORM)) {
|
||||
const blender::Mesh &b_mesh = *blender::id_cast<const blender::Mesh *>(b_ob_info.object_data);
|
||||
|
||||
float3 loc;
|
||||
float3 size;
|
||||
mesh_texture_space(b_mesh, loc, size);
|
||||
|
||||
Attribute *attr = volume->attributes.add(ATTR_STD_GENERATED_TRANSFORM);
|
||||
Transform *tfm = attr->data_for_write<Transform>();
|
||||
*tfm = transform_translate(-loc) * transform_scale(size);
|
||||
}
|
||||
}
|
||||
|
||||
class BlenderVolumeLoader : public VDBImageLoader {
|
||||
public:
|
||||
BlenderVolumeLoader(blender::Main &b_data,
|
||||
blender::Volume &b_volume,
|
||||
const string &grid_name,
|
||||
blender::VolumeRenderPrecision precision_,
|
||||
const float clipping)
|
||||
: VDBImageLoader(grid_name, clipping), b_volume(b_volume)
|
||||
{
|
||||
BKE_volume_load(&b_volume, &b_data);
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
for (const int grid_index : blender::IndexRange(BKE_volume_num_grids(&b_volume))) {
|
||||
const blender::bke::VolumeGridData &b_volume_grid = *BKE_volume_grid_get(&b_volume,
|
||||
grid_index);
|
||||
if (b_volume_grid.name() == grid_name) {
|
||||
b_volume_grid.add_user();
|
||||
volume_grid = blender::bke::GVolumeGrid{&b_volume_grid};
|
||||
grid = volume_grid->grid_ptr(tree_access_token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_NANOVDB
|
||||
switch (precision_) {
|
||||
case blender::VOLUME_PRECISION_FULL:
|
||||
precision = 32;
|
||||
break;
|
||||
case blender::VOLUME_PRECISION_HALF:
|
||||
precision = 16;
|
||||
break;
|
||||
default:
|
||||
case blender::VOLUME_PRECISION_VARIABLE:
|
||||
precision = 0;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
(void)precision_;
|
||||
#endif
|
||||
}
|
||||
|
||||
blender::Volume &b_volume;
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Store tree user so that the OPENVDB grid that is shared with Blender is not unloaded. */
|
||||
blender::bke::GVolumeGrid volume_grid;
|
||||
blender::bke::VolumeTreeAccessToken tree_access_token;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void sync_volume_object(blender::Main &b_data,
|
||||
blender::Scene &b_scene,
|
||||
BObjectInfo &b_ob_info,
|
||||
Scene *scene,
|
||||
Volume *volume)
|
||||
{
|
||||
blender::Volume &b_volume = *blender::id_cast<blender::Volume *>(b_ob_info.object_data);
|
||||
BKE_volume_load(&b_volume, &b_data);
|
||||
|
||||
blender::VolumeRender &b_render = b_volume.render;
|
||||
|
||||
volume->set_step_size(b_render.step_size);
|
||||
volume->set_object_space((b_render.space == blender::VOLUME_SPACE_OBJECT));
|
||||
|
||||
float velocity_scale = b_volume.velocity_scale;
|
||||
if (b_volume.velocity_unit == blender::CACHEFILE_VELOCITY_UNIT_SECOND) {
|
||||
/* Motion blur attribute is relative to seconds, we need it relative to frames. */
|
||||
const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
|
||||
const float motion_scale = (need_motion) ? scene->motion_shutter_time() /
|
||||
(b_scene.r.frs_sec / b_scene.r.frs_sec_base) :
|
||||
0.0f;
|
||||
|
||||
velocity_scale *= motion_scale;
|
||||
}
|
||||
|
||||
volume->set_velocity_scale(velocity_scale);
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Find grid with matching name. */
|
||||
for (const int grid_index : blender::IndexRange(BKE_volume_num_grids(&b_volume))) {
|
||||
const blender::bke::VolumeGridData &b_grid = *BKE_volume_grid_get(&b_volume, grid_index);
|
||||
const ustring name = ustring(b_grid.name());
|
||||
AttributeStandard std = ATTR_STD_NONE;
|
||||
|
||||
if (name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
|
||||
std = ATTR_STD_VOLUME_DENSITY;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
|
||||
std = ATTR_STD_VOLUME_COLOR;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
|
||||
std = ATTR_STD_VOLUME_FLAME;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
|
||||
std = ATTR_STD_VOLUME_HEAT;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
|
||||
std = ATTR_STD_VOLUME_TEMPERATURE;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) ||
|
||||
name == b_volume.velocity_grid)
|
||||
{
|
||||
std = ATTR_STD_VOLUME_VELOCITY;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_X) ||
|
||||
name == b_volume.runtime->velocity_x_grid)
|
||||
{
|
||||
std = ATTR_STD_VOLUME_VELOCITY_X;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_Y) ||
|
||||
name == b_volume.runtime->velocity_y_grid)
|
||||
{
|
||||
std = ATTR_STD_VOLUME_VELOCITY_Y;
|
||||
}
|
||||
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_Z) ||
|
||||
name == b_volume.runtime->velocity_z_grid)
|
||||
{
|
||||
std = ATTR_STD_VOLUME_VELOCITY_Z;
|
||||
}
|
||||
|
||||
const bool need_std = (std != ATTR_STD_NONE && volume->need_attribute(scene, std));
|
||||
const bool need_named = volume->need_attribute(scene, name);
|
||||
|
||||
if (need_std || need_named) {
|
||||
Attribute *attr;
|
||||
if (need_std) {
|
||||
/* Make grid available both with std and name. */
|
||||
attr = volume->attributes.add(std, name);
|
||||
}
|
||||
else {
|
||||
/* Make grid available by name with appropriate number of channels. */
|
||||
const int channels = blender::bke::volume_grid::get_channels_num(b_grid.grid_type());
|
||||
const TypeDesc type = (channels == 3) ? TypeVector : TypeFloat;
|
||||
attr = volume->attributes.add(name, type, ATTR_ELEMENT_VOXEL);
|
||||
}
|
||||
|
||||
unique_ptr<ImageLoader> loader = make_unique<BlenderVolumeLoader>(
|
||||
b_data,
|
||||
b_volume,
|
||||
name.string(),
|
||||
blender::VolumeRenderPrecision(b_render.precision),
|
||||
b_render.clipping);
|
||||
ImageParams params;
|
||||
params.frame = b_volume.runtime->frame;
|
||||
|
||||
attr->data_voxel_for_write() = scene->image_manager->add_image(
|
||||
std::move(loader), params, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create a matrix to transform from object space to normalized texture space [0, 1]. */
|
||||
if (volume->need_attribute(scene, ATTR_STD_GENERATED_TRANSFORM)) {
|
||||
std::optional<const blender::Bounds<blender::float3>> bounds = BKE_volume_min_max(&b_volume);
|
||||
|
||||
if (bounds.has_value()) {
|
||||
const blender::float3 size = bounds->size();
|
||||
const float3 loc = make_float3(bounds->min[0], bounds->min[1], bounds->min[2]);
|
||||
const float3 inv_size = safe_divide(one_float3(), make_float3(size[0], size[1], size[2]));
|
||||
|
||||
Attribute *attr = volume->attributes.add(ATTR_STD_GENERATED_TRANSFORM);
|
||||
Transform *tfm = attr->data_for_write<Transform>();
|
||||
*tfm = transform_scale(inv_size) * transform_translate(-loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume)
|
||||
{
|
||||
volume->clear(true);
|
||||
|
||||
if (view_layer.use_volumes) {
|
||||
if (GS(b_ob_info.object_data->name) == blender::ID_VO) {
|
||||
/* Volume object. Create only attributes, bounding mesh will then
|
||||
* be automatically generated later. */
|
||||
sync_volume_object(*b_data, *b_scene, b_ob_info, scene, volume);
|
||||
}
|
||||
else {
|
||||
/* Smoke domain. */
|
||||
sync_smoke_volume(*b_scene, scene, b_ob_info, volume, b_scene->r.cfra);
|
||||
}
|
||||
}
|
||||
|
||||
volume->merge_grids(scene);
|
||||
|
||||
/* Tag update. */
|
||||
volume->tag_update(scene, true);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
77
blender-5.2.0/intern/cycles/bvh/CMakeLists.txt
Normal file
77
blender-5.2.0/intern/cycles/bvh/CMakeLists.txt
Normal file
@@ -0,0 +1,77 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
octree.cpp
|
||||
bvh.cpp
|
||||
bvh2.cpp
|
||||
binning.cpp
|
||||
build.cpp
|
||||
embree.cpp
|
||||
hiprt.cpp
|
||||
multi.cpp
|
||||
node.cpp
|
||||
optix.cpp
|
||||
sort.cpp
|
||||
split.cpp
|
||||
unaligned.cpp
|
||||
)
|
||||
|
||||
set(SRC_METAL
|
||||
metal.mm
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_DEVICE_METAL)
|
||||
list(APPEND SRC
|
||||
${SRC_METAL}
|
||||
)
|
||||
add_definitions(-DWITH_METAL)
|
||||
endif()
|
||||
|
||||
set(SRC_HEADERS
|
||||
octree.h
|
||||
bvh.h
|
||||
bvh2.h
|
||||
binning.h
|
||||
build.h
|
||||
embree.h
|
||||
hiprt.h
|
||||
multi.h
|
||||
node.h
|
||||
optix.h
|
||||
params.h
|
||||
sort.h
|
||||
split.h
|
||||
unaligned.h
|
||||
metal.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PUBLIC cycles_scene
|
||||
PUBLIC cycles_util
|
||||
PUBLIC bf::dependencies::optional::embree
|
||||
)
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
if(WITH_CYCLES_EMBREE)
|
||||
list(APPEND LIB
|
||||
${EMBREE_LIBRARIES}
|
||||
)
|
||||
if(EMBREE_SYCL_SUPPORT)
|
||||
list(APPEND LIB
|
||||
${SYCL_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cycles_add_library(cycles_bvh "${LIB}" ${SRC} ${SRC_HEADERS})
|
||||
277
blender-5.2.0/intern/cycles/bvh/binning.cpp
Normal file
277
blender-5.2.0/intern/cycles/bvh/binning.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2011 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from Intel Corporation. */
|
||||
|
||||
// #define __KERNEL_SSE__
|
||||
|
||||
#include "bvh/binning.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/boundbox.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* SSE replacements */
|
||||
|
||||
__forceinline void prefetch_L1(const void * /*ptr*/) {}
|
||||
__forceinline void prefetch_L2(const void * /*ptr*/) {}
|
||||
__forceinline void prefetch_L3(const void * /*ptr*/) {}
|
||||
__forceinline void prefetch_NTA(const void * /*ptr*/) {}
|
||||
|
||||
template<size_t src> __forceinline float extract(const int4 &b)
|
||||
{
|
||||
return b[src];
|
||||
}
|
||||
template<size_t dst> __forceinline const float4 insert(const float4 &a, const float b)
|
||||
{
|
||||
float4 r = a;
|
||||
r[dst] = b;
|
||||
return r;
|
||||
}
|
||||
|
||||
__forceinline int get_best_dimension(const float4 &bestSAH)
|
||||
{
|
||||
// return (int)__bsf(movemask(reduce_min(bestSAH) == bestSAH));
|
||||
|
||||
const float minSAH = min(bestSAH.x, min(bestSAH.y, bestSAH.z));
|
||||
|
||||
if (bestSAH.x == minSAH) {
|
||||
return 0;
|
||||
}
|
||||
if (bestSAH.y == minSAH) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* BVH Object Binning */
|
||||
|
||||
BVHObjectBinning::BVHObjectBinning(const BVHRange &job,
|
||||
BVHReference *prims,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: BVHRange(job),
|
||||
splitSAH(FLT_MAX),
|
||||
dim(0),
|
||||
pos(0),
|
||||
unaligned_heuristic_(unaligned_heuristic),
|
||||
aligned_space_(aligned_space)
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
bounds_ = bounds();
|
||||
cent_bounds_ = cent_bounds();
|
||||
}
|
||||
else {
|
||||
/* TODO(sergey): With some additional storage we can avoid
|
||||
* need in re-calculating this.
|
||||
*/
|
||||
bounds_ = unaligned_heuristic->compute_aligned_boundbox(
|
||||
*this, prims, *aligned_space, ¢_bounds_);
|
||||
}
|
||||
|
||||
/* compute number of bins to use and precompute scaling factor for binning */
|
||||
num_bins = min(size_t(MAX_BINS), size_t(4.0f + 0.05f * size()));
|
||||
scale = safe_divide(make_float3((float)num_bins), cent_bounds_.size());
|
||||
|
||||
/* initialize binning counter and bounds */
|
||||
BoundBox bin_bounds[MAX_BINS][4]; /* bounds for every bin in every dimension */
|
||||
int4 bin_count[MAX_BINS]; /* number of primitives mapped to bin */
|
||||
|
||||
for (size_t i = 0; i < num_bins; i++) {
|
||||
bin_count[i] = make_int4(0);
|
||||
bin_bounds[i][0] = bin_bounds[i][1] = bin_bounds[i][2] = BoundBox::empty;
|
||||
}
|
||||
|
||||
/* map geometry to bins, unrolled once */
|
||||
{
|
||||
int64_t i;
|
||||
|
||||
for (i = 0; i < int64_t(size()) - 1; i += 2) {
|
||||
prefetch_L2(&prims[start() + i + 8]);
|
||||
|
||||
/* map even and odd primitive to bin */
|
||||
const BVHReference &prim0 = prims[start() + i + 0];
|
||||
const BVHReference &prim1 = prims[start() + i + 1];
|
||||
|
||||
const BoundBox bounds0 = get_prim_bounds(prim0);
|
||||
const BoundBox bounds1 = get_prim_bounds(prim1);
|
||||
|
||||
const int4 bin0 = get_bin(bounds0);
|
||||
const int4 bin1 = get_bin(bounds1);
|
||||
|
||||
/* increase bounds for bins for even primitive */
|
||||
const int b00 = (int)extract<0>(bin0);
|
||||
bin_count[b00][0]++;
|
||||
bin_bounds[b00][0].grow(bounds0);
|
||||
const int b01 = (int)extract<1>(bin0);
|
||||
bin_count[b01][1]++;
|
||||
bin_bounds[b01][1].grow(bounds0);
|
||||
const int b02 = (int)extract<2>(bin0);
|
||||
bin_count[b02][2]++;
|
||||
bin_bounds[b02][2].grow(bounds0);
|
||||
|
||||
/* increase bounds of bins for odd primitive */
|
||||
const int b10 = (int)extract<0>(bin1);
|
||||
bin_count[b10][0]++;
|
||||
bin_bounds[b10][0].grow(bounds1);
|
||||
const int b11 = (int)extract<1>(bin1);
|
||||
bin_count[b11][1]++;
|
||||
bin_bounds[b11][1].grow(bounds1);
|
||||
const int b12 = (int)extract<2>(bin1);
|
||||
bin_count[b12][2]++;
|
||||
bin_bounds[b12][2].grow(bounds1);
|
||||
}
|
||||
|
||||
/* for uneven number of primitives */
|
||||
if (i < int64_t(size())) {
|
||||
/* map primitive to bin */
|
||||
const BVHReference &prim0 = prims[start() + i];
|
||||
const BoundBox bounds0 = get_prim_bounds(prim0);
|
||||
const int4 bin0 = get_bin(bounds0);
|
||||
|
||||
/* increase bounds of bins */
|
||||
const int b00 = (int)extract<0>(bin0);
|
||||
bin_count[b00][0]++;
|
||||
bin_bounds[b00][0].grow(bounds0);
|
||||
const int b01 = (int)extract<1>(bin0);
|
||||
bin_count[b01][1]++;
|
||||
bin_bounds[b01][1].grow(bounds0);
|
||||
const int b02 = (int)extract<2>(bin0);
|
||||
bin_count[b02][2]++;
|
||||
bin_bounds[b02][2].grow(bounds0);
|
||||
}
|
||||
}
|
||||
|
||||
/* sweep from right to left and compute parallel prefix of merged bounds */
|
||||
float4 r_area[MAX_BINS]; /* area of bounds of primitives on the right */
|
||||
float4 r_count[MAX_BINS]; /* number of primitives on the right */
|
||||
int4 count = make_int4(0);
|
||||
|
||||
BoundBox bx = BoundBox::empty;
|
||||
BoundBox by = BoundBox::empty;
|
||||
BoundBox bz = BoundBox::empty;
|
||||
|
||||
for (size_t i = num_bins - 1; i > 0; i--) {
|
||||
count = count + bin_count[i];
|
||||
r_count[i] = blocks(count);
|
||||
|
||||
bx = merge(bx, bin_bounds[i][0]);
|
||||
r_area[i][0] = bx.half_area();
|
||||
by = merge(by, bin_bounds[i][1]);
|
||||
r_area[i][1] = by.half_area();
|
||||
bz = merge(bz, bin_bounds[i][2]);
|
||||
r_area[i][2] = bz.half_area();
|
||||
r_area[i][3] = r_area[i][2];
|
||||
}
|
||||
|
||||
/* sweep from left to right and compute SAH */
|
||||
int4 ii = make_int4(1);
|
||||
float4 bestSAH = make_float4(FLT_MAX);
|
||||
int4 bestSplit = make_int4(-1);
|
||||
|
||||
count = make_int4(0);
|
||||
|
||||
bx = BoundBox::empty;
|
||||
by = BoundBox::empty;
|
||||
bz = BoundBox::empty;
|
||||
|
||||
for (size_t i = 1; i < num_bins; i++, ii += make_int4(1)) {
|
||||
count = count + bin_count[i - 1];
|
||||
|
||||
bx = merge(bx, bin_bounds[i - 1][0]);
|
||||
const float Ax = bx.half_area();
|
||||
by = merge(by, bin_bounds[i - 1][1]);
|
||||
const float Ay = by.half_area();
|
||||
bz = merge(bz, bin_bounds[i - 1][2]);
|
||||
const float Az = bz.half_area();
|
||||
|
||||
const float4 lCount = blocks(count);
|
||||
const float4 lArea = make_float4(Ax, Ay, Az, Az);
|
||||
const float4 sah = lArea * lCount + r_area[i] * r_count[i];
|
||||
|
||||
bestSplit = select(sah < bestSAH, ii, bestSplit);
|
||||
bestSAH = min(sah, bestSAH);
|
||||
}
|
||||
|
||||
const int4 mask = make_float4(cent_bounds_.size()) <= zero_float4();
|
||||
bestSAH = insert<3>(select(mask, make_float4(FLT_MAX), bestSAH), FLT_MAX);
|
||||
|
||||
/* find best dimension */
|
||||
dim = get_best_dimension(bestSAH);
|
||||
splitSAH = bestSAH[dim];
|
||||
pos = bestSplit[dim];
|
||||
leafSAH = bounds_.half_area() * blocks(size());
|
||||
}
|
||||
|
||||
void BVHObjectBinning::split(BVHReference *prims,
|
||||
BVHObjectBinning &left_o,
|
||||
BVHObjectBinning &right_o) const
|
||||
{
|
||||
const size_t N = size();
|
||||
|
||||
BoundBox lgeom_bounds = BoundBox::empty;
|
||||
BoundBox rgeom_bounds = BoundBox::empty;
|
||||
BoundBox lcent_bounds = BoundBox::empty;
|
||||
BoundBox rcent_bounds = BoundBox::empty;
|
||||
|
||||
int64_t l = 0;
|
||||
int64_t r = N - 1;
|
||||
|
||||
while (l <= r) {
|
||||
prefetch_L2(&prims[start() + l + 8]);
|
||||
prefetch_L2(&prims[start() + r - 8]);
|
||||
|
||||
const BVHReference prim = prims[start() + l];
|
||||
const BoundBox unaligned_bounds = get_prim_bounds(prim);
|
||||
const float3 unaligned_center = unaligned_bounds.center2();
|
||||
const float3 center = prim.bounds().center2();
|
||||
|
||||
if (get_bin(unaligned_center)[dim] < pos) {
|
||||
lgeom_bounds.grow(prim.bounds());
|
||||
lcent_bounds.grow(center);
|
||||
l++;
|
||||
}
|
||||
else {
|
||||
rgeom_bounds.grow(prim.bounds());
|
||||
rcent_bounds.grow(center);
|
||||
swap(prims[start() + l], prims[start() + r]);
|
||||
r--;
|
||||
}
|
||||
}
|
||||
/* finish */
|
||||
if (l != 0 && N - 1 - r != 0) {
|
||||
right_o = BVHObjectBinning(BVHRange(rgeom_bounds, rcent_bounds, start() + l, N - 1 - r),
|
||||
prims);
|
||||
left_o = BVHObjectBinning(BVHRange(lgeom_bounds, lcent_bounds, start(), l), prims);
|
||||
return;
|
||||
}
|
||||
|
||||
/* object medium split if we did not make progress, can happen when all
|
||||
* primitives have same centroid */
|
||||
lgeom_bounds = BoundBox::empty;
|
||||
rgeom_bounds = BoundBox::empty;
|
||||
lcent_bounds = BoundBox::empty;
|
||||
rcent_bounds = BoundBox::empty;
|
||||
|
||||
for (size_t i = 0; i < N / 2; i++) {
|
||||
lgeom_bounds.grow(prims[start() + i].bounds());
|
||||
lcent_bounds.grow(prims[start() + i].bounds().center2());
|
||||
}
|
||||
|
||||
for (size_t i = N / 2; i < N; i++) {
|
||||
rgeom_bounds.grow(prims[start() + i].bounds());
|
||||
rcent_bounds.grow(prims[start() + i].bounds().center2());
|
||||
}
|
||||
|
||||
right_o = BVHObjectBinning(BVHRange(rgeom_bounds, rcent_bounds, start() + N / 2, N / 2 + N % 2),
|
||||
prims);
|
||||
left_o = BVHObjectBinning(BVHRange(lgeom_bounds, lcent_bounds, start(), N / 2), prims);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
98
blender-5.2.0/intern/cycles/bvh/binning.h
Normal file
98
blender-5.2.0/intern/cycles/bvh/binning.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2011 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from Intel Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHBuild;
|
||||
|
||||
/* Single threaded object binner. Finds the split with the best SAH heuristic
|
||||
* by testing for each dimension multiple partitionings for regular spaced
|
||||
* partition locations. A partitioning for a partition location is computed,
|
||||
* by putting primitives whose centroid is on the left and right of the split
|
||||
* location to different sets. The SAH is evaluated by computing the number of
|
||||
* blocks occupied by the primitives in the partitions. */
|
||||
|
||||
class BVHObjectBinning : public BVHRange {
|
||||
public:
|
||||
__forceinline BVHObjectBinning() : leafSAH(FLT_MAX) {}
|
||||
|
||||
BVHObjectBinning(const BVHRange &job,
|
||||
BVHReference *prims,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr);
|
||||
|
||||
void split(BVHReference *prims, BVHObjectBinning &left_o, BVHObjectBinning &right_o) const;
|
||||
|
||||
__forceinline const BoundBox &unaligned_bounds()
|
||||
{
|
||||
return bounds_;
|
||||
}
|
||||
|
||||
float splitSAH; /* SAH cost of the best split */
|
||||
float leafSAH; /* SAH cost of creating a leaf */
|
||||
|
||||
protected:
|
||||
int dim; /* best split dimension */
|
||||
int pos; /* best split position */
|
||||
size_t num_bins; /* actual number of bins to use */
|
||||
float3 scale; /* scaling factor to compute bin */
|
||||
|
||||
/* Effective bounds and centroid bounds. */
|
||||
BoundBox bounds_;
|
||||
BoundBox cent_bounds_;
|
||||
|
||||
const BVHUnaligned *unaligned_heuristic_;
|
||||
const Transform *aligned_space_;
|
||||
|
||||
enum { MAX_BINS = 32 };
|
||||
enum { LOG_BLOCK_SIZE = 2 };
|
||||
|
||||
/* computes the bin numbers for each dimension for a box. */
|
||||
__forceinline int4 get_bin(const BoundBox &box) const
|
||||
{
|
||||
const int4 a = make_int4((box.center2() - cent_bounds_.min) * scale - make_float3(0.5f));
|
||||
const int4 mn = make_int4(0);
|
||||
const int4 mx = make_int4((int)num_bins - 1);
|
||||
|
||||
return clamp(a, mn, mx);
|
||||
}
|
||||
|
||||
/* computes the bin numbers for each dimension for a point. */
|
||||
__forceinline int4 get_bin(const float3 &c) const
|
||||
{
|
||||
return make_int4((c - cent_bounds_.min) * scale - make_float3(0.5f));
|
||||
}
|
||||
|
||||
/* compute the number of blocks occupied for each dimension. */
|
||||
__forceinline float4 blocks(const int4 &a) const
|
||||
{
|
||||
return make_float4((a + make_int4((1 << LOG_BLOCK_SIZE) - 1)) >> LOG_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/* compute the number of blocks occupied in one dimension. */
|
||||
__forceinline int blocks(const size_t a) const
|
||||
{
|
||||
return (int)((a + ((1LL << LOG_BLOCK_SIZE) - 1)) >> LOG_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
|
||||
{
|
||||
if (aligned_space_ == nullptr) {
|
||||
return prim.bounds();
|
||||
}
|
||||
return unaligned_heuristic_->compute_aligned_prim_boundbox(prim, *aligned_space_);
|
||||
}
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
1291
blender-5.2.0/intern/cycles/bvh/build.cpp
Normal file
1291
blender-5.2.0/intern/cycles/bvh/build.cpp
Normal file
File diff suppressed because it is too large
Load Diff
144
blender-5.2.0/intern/cycles/bvh/build.h
Normal file
144
blender-5.2.0/intern/cycles/bvh/build.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/task.h"
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Boundbox;
|
||||
class BVHBuildTask;
|
||||
class BVHNode;
|
||||
class BVHSpatialSplitBuildTask;
|
||||
class BVHParams;
|
||||
class InnerNode;
|
||||
class Geometry;
|
||||
class Hair;
|
||||
class Mesh;
|
||||
class Object;
|
||||
class PointCloud;
|
||||
class Progress;
|
||||
|
||||
/* BVH Builder */
|
||||
|
||||
class BVHBuild {
|
||||
public:
|
||||
/* Constructor/Destructor */
|
||||
BVHBuild(const vector<Object *> &objects,
|
||||
array<int> &prim_type,
|
||||
array<int> &prim_index,
|
||||
array<int> &prim_object,
|
||||
array<float2> &prim_time,
|
||||
const BVHParams ¶ms,
|
||||
Progress &progress);
|
||||
~BVHBuild();
|
||||
|
||||
unique_ptr<BVHNode> run();
|
||||
|
||||
protected:
|
||||
friend class BVHMixedSplit;
|
||||
friend class BVHObjectSplit;
|
||||
friend class BVHSpatialSplit;
|
||||
friend class BVHBuildTask;
|
||||
friend class BVHSpatialSplitBuildTask;
|
||||
friend class BVHObjectBinning;
|
||||
|
||||
/* Adding references. */
|
||||
void add_reference_triangles(BoundBox &root,
|
||||
BoundBox ¢er,
|
||||
Mesh *mesh,
|
||||
const int object_index);
|
||||
void add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair, const int object_index);
|
||||
void add_reference_points(BoundBox &root, BoundBox ¢er, PointCloud *pointcloud, const int i);
|
||||
void add_reference_geometry(BoundBox &root,
|
||||
BoundBox ¢er,
|
||||
Geometry *geom,
|
||||
const int object_index);
|
||||
void add_reference_object(BoundBox &root, BoundBox ¢er, Object *ob, const int i);
|
||||
void add_references(BVHRange &root);
|
||||
|
||||
/* Building. */
|
||||
unique_ptr<BVHNode> build_node(const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const int level,
|
||||
BVHSpatialStorage *storage);
|
||||
unique_ptr<BVHNode> build_node(const BVHObjectBinning &range, const int level);
|
||||
unique_ptr<BVHNode> create_leaf_node(const BVHRange &range,
|
||||
const vector<BVHReference> &references);
|
||||
unique_ptr<BVHNode> create_object_leaf_nodes(const BVHReference *ref,
|
||||
const int start,
|
||||
const int num);
|
||||
|
||||
bool range_within_max_leaf_size(const BVHRange &range,
|
||||
const vector<BVHReference> &references) const;
|
||||
|
||||
/* Threads. */
|
||||
enum { THREAD_TASK_SIZE = 4096 };
|
||||
void thread_build_node(InnerNode *inner,
|
||||
const int child,
|
||||
const BVHObjectBinning &range,
|
||||
const int level);
|
||||
void thread_build_spatial_split_node(InnerNode *inner,
|
||||
const int child,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
int level);
|
||||
thread_mutex build_mutex;
|
||||
|
||||
/* Progress. */
|
||||
void progress_update();
|
||||
|
||||
/* Tree rotations. */
|
||||
void rotate(BVHNode *node, const int max_depth);
|
||||
void rotate(BVHNode *node, const int max_depth, const int iterations);
|
||||
|
||||
/* Objects and primitive references. */
|
||||
vector<Object *> objects;
|
||||
vector<BVHReference> references;
|
||||
int num_original_references;
|
||||
|
||||
/* Output primitive indexes and objects. */
|
||||
array<int> &prim_type;
|
||||
array<int> &prim_index;
|
||||
array<int> &prim_object;
|
||||
array<float2> &prim_time;
|
||||
|
||||
bool need_prim_time;
|
||||
|
||||
/* Build parameters. */
|
||||
BVHParams params;
|
||||
|
||||
/* Progress reporting. */
|
||||
Progress &progress;
|
||||
double progress_start_time;
|
||||
size_t progress_count;
|
||||
size_t progress_total;
|
||||
size_t progress_original_total;
|
||||
|
||||
/* Spatial splitting. */
|
||||
float spatial_min_overlap;
|
||||
enumerable_thread_specific<BVHSpatialStorage> spatial_storage;
|
||||
size_t spatial_free_index;
|
||||
thread_spin_lock spatial_spin_lock;
|
||||
|
||||
/* Threads. */
|
||||
TaskPool task_pool;
|
||||
|
||||
/* Unaligned building. */
|
||||
BVHUnaligned unaligned_heuristic;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
150
blender-5.2.0/intern/cycles/bvh/bvh.cpp
Normal file
150
blender-5.2.0/intern/cycles/bvh/bvh.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
#include "bvh/multi.h"
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
# include "bvh/embree.h"
|
||||
#endif
|
||||
#ifdef WITH_HIPRT
|
||||
# include "bvh/hiprt.h"
|
||||
#endif
|
||||
#ifdef WITH_METAL
|
||||
# include "bvh/metal.h"
|
||||
#endif
|
||||
#ifdef WITH_OPTIX
|
||||
# include "bvh/optix.h"
|
||||
#endif
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* BVH Parameters. */
|
||||
|
||||
const char *bvh_layout_name(BVHLayout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case BVH_LAYOUT_NONE:
|
||||
return "NONE";
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return "BVH2";
|
||||
case BVH_LAYOUT_EMBREE:
|
||||
return "EMBREE";
|
||||
case BVH_LAYOUT_OPTIX:
|
||||
return "OPTIX";
|
||||
case BVH_LAYOUT_METAL:
|
||||
return "METAL";
|
||||
case BVH_LAYOUT_HIPRT:
|
||||
return "HIPRT";
|
||||
case BVH_LAYOUT_EMBREEGPU:
|
||||
return "EMBREEGPU";
|
||||
case BVH_LAYOUT_MULTI_OPTIX:
|
||||
case BVH_LAYOUT_MULTI_METAL:
|
||||
case BVH_LAYOUT_MULTI_HIPRT:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU:
|
||||
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_METAL_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_HIPRT_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU_EMBREE:
|
||||
return "MULTI";
|
||||
case BVH_LAYOUT_ALL:
|
||||
return "ALL";
|
||||
}
|
||||
LOG_DFATAL << "Unsupported BVH layout was passed.";
|
||||
return "";
|
||||
}
|
||||
|
||||
BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask supported_layouts)
|
||||
{
|
||||
const BVHLayoutMask requested_layout_mask = (BVHLayoutMask)requested_layout;
|
||||
/* Check whether requested layout is supported, if so -- no need to do
|
||||
* any extra computation.
|
||||
*/
|
||||
if (supported_layouts & requested_layout_mask) {
|
||||
return requested_layout;
|
||||
}
|
||||
/* Some bit magic to get widest supported BVH layout. */
|
||||
/* This is a mask of supported BVH layouts which are narrower than the
|
||||
* requested one.
|
||||
*/
|
||||
BVHLayoutMask allowed_layouts_mask = (supported_layouts & (requested_layout_mask - 1));
|
||||
/* If the requested layout is not supported, choose from the supported layouts instead. */
|
||||
if (allowed_layouts_mask == 0) {
|
||||
allowed_layouts_mask = supported_layouts;
|
||||
}
|
||||
/* We get widest from allowed ones and convert mask to actual layout. */
|
||||
const BVHLayoutMask widest_allowed_layout_mask = __bsr((uint32_t)allowed_layouts_mask);
|
||||
return (BVHLayout)(1 << widest_allowed_layout_mask);
|
||||
}
|
||||
|
||||
/* BVH */
|
||||
|
||||
BVH::BVH(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: params(params_), geometry(geometry_), objects(objects_)
|
||||
{
|
||||
}
|
||||
|
||||
unique_ptr<BVH> BVH::create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device)
|
||||
{
|
||||
switch (params.bvh_layout) {
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return make_unique<BVH2>(params, geometry, objects);
|
||||
case BVH_LAYOUT_EMBREE:
|
||||
case BVH_LAYOUT_EMBREEGPU:
|
||||
#ifdef WITH_EMBREE
|
||||
return make_unique<BVHEmbree>(params, geometry, objects);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_OPTIX:
|
||||
#ifdef WITH_OPTIX
|
||||
return make_unique<BVHOptiX>(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_METAL:
|
||||
#ifdef WITH_METAL
|
||||
return bvh_metal_create(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_HIPRT:
|
||||
#ifdef WITH_HIPRT
|
||||
return make_unique<BVHHIPRT>(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_MULTI_OPTIX:
|
||||
case BVH_LAYOUT_MULTI_METAL:
|
||||
case BVH_LAYOUT_MULTI_HIPRT:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU:
|
||||
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_METAL_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_HIPRT_EMBREE:
|
||||
case BVH_LAYOUT_MULTI_EMBREEGPU_EMBREE:
|
||||
return make_unique<BVHMulti>(params, geometry, objects);
|
||||
case BVH_LAYOUT_NONE:
|
||||
case BVH_LAYOUT_ALL:
|
||||
break;
|
||||
}
|
||||
LOG_DFATAL << "Requested unsupported BVH layout.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
92
blender-5.2.0/intern/cycles/bvh/bvh.h
Normal file
92
blender-5.2.0/intern/cycles/bvh/bvh.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "util/array.h"
|
||||
#include "util/types.h"
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BoundBox;
|
||||
class BVHNode;
|
||||
class BVHParams;
|
||||
class Device;
|
||||
class DeviceScene;
|
||||
class Geometry;
|
||||
class LeafNode;
|
||||
class Object;
|
||||
class Progress;
|
||||
class Stats;
|
||||
|
||||
#define BVH_ALIGN 4096 // NOLINT
|
||||
#define TRI_NODE_SIZE 3 // NOLINT
|
||||
|
||||
/* Packed BVH
|
||||
*
|
||||
* BVH stored as it will be used for traversal on the rendering device. */
|
||||
|
||||
struct PackedBVH {
|
||||
/* BVH nodes storage, one node is 4x int4, and contains two bounding boxes,
|
||||
* and child, triangle or object indexes depending on the node type */
|
||||
array<int4> nodes;
|
||||
/* BVH leaf nodes storage. */
|
||||
array<int4> leaf_nodes;
|
||||
/* object index to BVH node index mapping for instances */
|
||||
array<int> object_node;
|
||||
/* primitive type - triangle or strand */
|
||||
array<int> prim_type;
|
||||
/* Visibility visibilities for primitives. */
|
||||
array<uint> prim_visibility;
|
||||
/* mapping from BVH primitive index to true primitive index, as primitives
|
||||
* may be duplicated due to spatial splits. -1 for instances. */
|
||||
array<int> prim_index;
|
||||
/* mapping from BVH primitive index, to the object id of that primitive. */
|
||||
array<int> prim_object;
|
||||
/* Time range of BVH primitive. */
|
||||
array<float2> prim_time;
|
||||
|
||||
/* index of the root node. */
|
||||
int root_index;
|
||||
|
||||
PackedBVH()
|
||||
{
|
||||
root_index = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* BVH */
|
||||
|
||||
class BVH {
|
||||
public:
|
||||
BVHParams params;
|
||||
vector<Geometry *> geometry;
|
||||
vector<Object *> objects;
|
||||
|
||||
static unique_ptr<BVH> create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
virtual ~BVH() = default;
|
||||
|
||||
virtual void replace_geometry(const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects)
|
||||
{
|
||||
this->geometry = geometry;
|
||||
this->objects = objects;
|
||||
}
|
||||
|
||||
protected:
|
||||
BVH(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
644
blender-5.2.0/intern/cycles/bvh/bvh2.cpp
Normal file
644
blender-5.2.0/intern/cycles/bvh/bvh2.cpp
Normal file
@@ -0,0 +1,644 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
|
||||
#include "scene/hair.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/pointcloud.h"
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/node.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/progress.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHStackEntry::BVHStackEntry(const BVHNode *n, const int i) : node(n), idx(i) {}
|
||||
|
||||
int BVHStackEntry::encodeIdx() const
|
||||
{
|
||||
return (node->is_leaf()) ? ~idx : idx;
|
||||
}
|
||||
|
||||
BVH2::BVH2(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_)
|
||||
{
|
||||
}
|
||||
|
||||
void BVH2::build(Progress &progress, Stats * /*unused*/)
|
||||
{
|
||||
progress.set_substatus("Building BVH");
|
||||
|
||||
/* build nodes */
|
||||
BVHBuild bvh_build(objects,
|
||||
pack.prim_type,
|
||||
pack.prim_index,
|
||||
pack.prim_object,
|
||||
pack.prim_time,
|
||||
params,
|
||||
progress);
|
||||
unique_ptr<BVHNode> bvh2_root = bvh_build.run();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* BVH builder returns tree in a binary mode (with two children per inner
|
||||
* node. Need to adopt that for a wider BVH implementations. */
|
||||
const unique_ptr<BVHNode> root = widen_children_nodes(std::move(bvh2_root));
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack triangles */
|
||||
progress.set_substatus("Packing BVH triangles and strands");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack nodes */
|
||||
progress.set_substatus("Packing BVH nodes");
|
||||
pack_nodes(root.get());
|
||||
}
|
||||
|
||||
void BVH2::refit(Progress &progress)
|
||||
{
|
||||
progress.set_substatus("Packing BVH primitives");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
refit_nodes();
|
||||
}
|
||||
|
||||
unique_ptr<BVHNode> BVH2::widen_children_nodes(unique_ptr<BVHNode> &&root)
|
||||
{
|
||||
return std::move(root);
|
||||
}
|
||||
|
||||
void BVH2::pack_leaf(const BVHStackEntry &e, const LeafNode *leaf)
|
||||
{
|
||||
assert(e.idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
|
||||
int4 data[BVH_NODE_LEAF_SIZE];
|
||||
std::fill_n(data, BVH_NODE_LEAF_SIZE, zero_int4());
|
||||
if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
|
||||
/* object */
|
||||
data[0].x = ~(leaf->lo);
|
||||
data[0].y = 0;
|
||||
}
|
||||
else {
|
||||
/* triangle */
|
||||
data[0].x = leaf->lo;
|
||||
data[0].y = leaf->hi;
|
||||
}
|
||||
data[0].z = leaf->visibility;
|
||||
if (leaf->num_triangles() != 0) {
|
||||
data[0].w = pack.prim_type[leaf->lo];
|
||||
}
|
||||
|
||||
std::copy_n(data, BVH_NODE_LEAF_SIZE, &pack.leaf_nodes[e.idx]);
|
||||
}
|
||||
|
||||
void BVH2::pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1)
|
||||
{
|
||||
if (e0.node->is_unaligned || e1.node->is_unaligned) {
|
||||
pack_unaligned_inner(e, e0, e1);
|
||||
}
|
||||
else {
|
||||
pack_aligned_inner(e, e0, e1);
|
||||
}
|
||||
}
|
||||
|
||||
void BVH2::pack_aligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1)
|
||||
{
|
||||
pack_aligned_node(e.idx,
|
||||
e0.node->bounds,
|
||||
e1.node->bounds,
|
||||
e0.encodeIdx(),
|
||||
e1.encodeIdx(),
|
||||
e0.node->visibility,
|
||||
e1.node->visibility);
|
||||
}
|
||||
|
||||
void BVH2::pack_aligned_node(const int idx,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1)
|
||||
{
|
||||
assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
|
||||
assert(c0 < 0 || c0 < pack.nodes.size());
|
||||
assert(c1 < 0 || c1 < pack.nodes.size());
|
||||
|
||||
int4 data[BVH_NODE_SIZE] = {
|
||||
make_int4(visibility0 & ~PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
visibility1 & ~PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
c0,
|
||||
c1),
|
||||
make_int4(__float_as_int(b0.min.x),
|
||||
__float_as_int(b1.min.x),
|
||||
__float_as_int(b0.max.x),
|
||||
__float_as_int(b1.max.x)),
|
||||
make_int4(__float_as_int(b0.min.y),
|
||||
__float_as_int(b1.min.y),
|
||||
__float_as_int(b0.max.y),
|
||||
__float_as_int(b1.max.y)),
|
||||
make_int4(__float_as_int(b0.min.z),
|
||||
__float_as_int(b1.min.z),
|
||||
__float_as_int(b0.max.z),
|
||||
__float_as_int(b1.max.z)),
|
||||
};
|
||||
|
||||
std::copy_n(data, BVH_NODE_SIZE, &pack.nodes[idx]);
|
||||
}
|
||||
|
||||
void BVH2::pack_unaligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1)
|
||||
{
|
||||
pack_unaligned_node(e.idx,
|
||||
e0.node->get_aligned_space(),
|
||||
e1.node->get_aligned_space(),
|
||||
e0.node->bounds,
|
||||
e1.node->bounds,
|
||||
e0.encodeIdx(),
|
||||
e1.encodeIdx(),
|
||||
e0.node->visibility,
|
||||
e1.node->visibility);
|
||||
}
|
||||
|
||||
void BVH2::pack_unaligned_node(const int idx,
|
||||
const Transform &aligned_space0,
|
||||
const Transform &aligned_space1,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1)
|
||||
{
|
||||
assert(idx + BVH_UNALIGNED_NODE_SIZE <= pack.nodes.size());
|
||||
assert(c0 < 0 || c0 < pack.nodes.size());
|
||||
assert(c1 < 0 || c1 < pack.nodes.size());
|
||||
|
||||
int4 data[BVH_UNALIGNED_NODE_SIZE];
|
||||
const Transform space0 = BVHUnaligned::compute_node_transform(b0, aligned_space0);
|
||||
const Transform space1 = BVHUnaligned::compute_node_transform(b1, aligned_space1);
|
||||
data[0] = make_int4(visibility0 | PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
visibility1 | PATH_RAY_VISIBILITY_NODE_UNALIGNED,
|
||||
c0,
|
||||
c1);
|
||||
|
||||
data[1] = __float4_as_int4(space0.x);
|
||||
data[2] = __float4_as_int4(space0.y);
|
||||
data[3] = __float4_as_int4(space0.z);
|
||||
data[4] = __float4_as_int4(space1.x);
|
||||
data[5] = __float4_as_int4(space1.y);
|
||||
data[6] = __float4_as_int4(space1.z);
|
||||
|
||||
std::copy_n(data, BVH_UNALIGNED_NODE_SIZE, &pack.nodes[idx]);
|
||||
}
|
||||
|
||||
void BVH2::pack_nodes(const BVHNode *root)
|
||||
{
|
||||
const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
|
||||
const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
|
||||
assert(num_leaf_nodes <= num_nodes);
|
||||
const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
|
||||
size_t node_size;
|
||||
if (params.use_unaligned_nodes) {
|
||||
const size_t num_unaligned_nodes = root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
|
||||
node_size = (num_unaligned_nodes * BVH_UNALIGNED_NODE_SIZE) +
|
||||
(num_inner_nodes - num_unaligned_nodes) * BVH_NODE_SIZE;
|
||||
}
|
||||
else {
|
||||
node_size = num_inner_nodes * BVH_NODE_SIZE;
|
||||
}
|
||||
/* Resize arrays */
|
||||
pack.nodes.clear();
|
||||
pack.leaf_nodes.clear();
|
||||
/* For top level BVH, first merge existing BVH's so we know the offsets. */
|
||||
if (params.top_level) {
|
||||
pack_instances(node_size, num_leaf_nodes * BVH_NODE_LEAF_SIZE);
|
||||
}
|
||||
else {
|
||||
pack.nodes.resize(node_size);
|
||||
pack.leaf_nodes.resize(num_leaf_nodes * BVH_NODE_LEAF_SIZE);
|
||||
}
|
||||
|
||||
int nextNodeIdx = 0;
|
||||
int nextLeafNodeIdx = 0;
|
||||
|
||||
vector<BVHStackEntry> stack;
|
||||
stack.reserve(BVHParams::MAX_DEPTH * 2);
|
||||
if (root->is_leaf()) {
|
||||
stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
|
||||
}
|
||||
else {
|
||||
stack.push_back(BVHStackEntry(root, nextNodeIdx));
|
||||
nextNodeIdx += root->has_unaligned() ? BVH_UNALIGNED_NODE_SIZE : BVH_NODE_SIZE;
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
const BVHStackEntry e = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
if (e.node->is_leaf()) {
|
||||
/* leaf node */
|
||||
const LeafNode *leaf = reinterpret_cast<const LeafNode *>(e.node);
|
||||
pack_leaf(e, leaf);
|
||||
}
|
||||
else {
|
||||
/* inner node */
|
||||
int idx[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (e.node->get_child(i)->is_leaf()) {
|
||||
idx[i] = nextLeafNodeIdx++;
|
||||
}
|
||||
else {
|
||||
idx[i] = nextNodeIdx;
|
||||
nextNodeIdx += e.node->get_child(i)->has_unaligned() ? BVH_UNALIGNED_NODE_SIZE :
|
||||
BVH_NODE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
stack.push_back(BVHStackEntry(e.node->get_child(0), idx[0]));
|
||||
stack.push_back(BVHStackEntry(e.node->get_child(1), idx[1]));
|
||||
|
||||
pack_inner(e, stack[stack.size() - 2], stack[stack.size() - 1]);
|
||||
}
|
||||
}
|
||||
assert(node_size == nextNodeIdx);
|
||||
/* root index to start traversal at, to handle case of single leaf node */
|
||||
pack.root_index = (root->is_leaf()) ? -1 : 0;
|
||||
}
|
||||
|
||||
void BVH2::refit_nodes()
|
||||
{
|
||||
assert(!params.top_level);
|
||||
|
||||
BoundBox bbox = BoundBox::empty;
|
||||
uint visibility = 0;
|
||||
refit_node(0, (pack.root_index == -1) ? true : false, bbox, visibility);
|
||||
}
|
||||
|
||||
void BVH2::refit_node(const int idx, bool leaf, BoundBox &bbox, uint &visibility)
|
||||
{
|
||||
if (leaf) {
|
||||
/* refit leaf node */
|
||||
assert(idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
|
||||
const int4 *data = &pack.leaf_nodes[idx];
|
||||
const int c0 = data[0].x;
|
||||
const int c1 = data[0].y;
|
||||
|
||||
refit_primitives(c0, c1, bbox, visibility);
|
||||
|
||||
/* TODO(sergey): De-duplicate with pack_leaf(). */
|
||||
int4 leaf_data[BVH_NODE_LEAF_SIZE];
|
||||
leaf_data[0].x = c0;
|
||||
leaf_data[0].y = c1;
|
||||
leaf_data[0].z = visibility;
|
||||
leaf_data[0].w = data[0].w;
|
||||
std::copy_n(leaf_data, BVH_NODE_LEAF_SIZE, &pack.leaf_nodes[idx]);
|
||||
}
|
||||
else {
|
||||
assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
|
||||
|
||||
const int4 *data = &pack.nodes[idx];
|
||||
const bool is_unaligned = (data[0].x & PATH_RAY_VISIBILITY_NODE_UNALIGNED) != 0;
|
||||
const int c0 = data[0].z;
|
||||
const int c1 = data[0].w;
|
||||
/* refit inner node, set bbox from children */
|
||||
BoundBox bbox0 = BoundBox::empty;
|
||||
BoundBox bbox1 = BoundBox::empty;
|
||||
uint visibility0 = 0;
|
||||
uint visibility1 = 0;
|
||||
|
||||
refit_node((c0 < 0) ? -c0 - 1 : c0, (c0 < 0), bbox0, visibility0);
|
||||
refit_node((c1 < 0) ? -c1 - 1 : c1, (c1 < 0), bbox1, visibility1);
|
||||
|
||||
if (is_unaligned) {
|
||||
const Transform aligned_space = transform_identity();
|
||||
pack_unaligned_node(
|
||||
idx, aligned_space, aligned_space, bbox0, bbox1, c0, c1, visibility0, visibility1);
|
||||
}
|
||||
else {
|
||||
pack_aligned_node(idx, bbox0, bbox1, c0, c1, visibility0, visibility1);
|
||||
}
|
||||
|
||||
bbox.grow(bbox0);
|
||||
bbox.grow(bbox1);
|
||||
visibility = visibility0 | visibility1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Refitting */
|
||||
|
||||
void BVH2::refit_primitives(const int start, const int end, BoundBox &bbox, uint &visibility)
|
||||
{
|
||||
/* Refit range of primitives. */
|
||||
for (int prim = start; prim < end; prim++) {
|
||||
const int pidx = pack.prim_index[prim];
|
||||
const int tob = pack.prim_object[prim];
|
||||
Object *ob = objects[tob];
|
||||
|
||||
if (pidx == -1) {
|
||||
/* Object instance. */
|
||||
bbox.grow(ob->bounds);
|
||||
}
|
||||
else {
|
||||
/* Primitives. */
|
||||
if (pack.prim_type[prim] & PRIMITIVE_CURVE) {
|
||||
/* Curves. */
|
||||
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
|
||||
const int prim_offset = (params.top_level) ? hair->prim_offset : 0;
|
||||
const Hair::Curve curve = hair->get_curve(pidx - prim_offset);
|
||||
const int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
|
||||
|
||||
curve.bounds_grow(k, hair->get_position(), hair->get_radius(), bbox);
|
||||
|
||||
/* Motion curves. */
|
||||
if (hair->get_use_motion_blur()) {
|
||||
const Attribute *attr_P = hair->attributes.find(ATTR_STD_POSITION);
|
||||
const Attribute *attr_R = hair->attributes.find(ATTR_STD_RADIUS);
|
||||
|
||||
if (attr_P->has_motion()) {
|
||||
for (int attr_step = 1; attr_step < attr_P->num_motion_steps(); attr_step++) {
|
||||
curve.bounds_grow(
|
||||
k, attr_P->data<packed_float3>(attr_step), attr_R->data<float>(attr_step), bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pack.prim_type[prim] & PRIMITIVE_POINT) {
|
||||
/* Points. */
|
||||
const PointCloud *pointcloud = static_cast<const PointCloud *>(ob->get_geometry());
|
||||
const int prim_offset = (params.top_level) ? pointcloud->prim_offset : 0;
|
||||
const packed_float3 *points = pointcloud->get_position();
|
||||
const float *radius = pointcloud->get_radius();
|
||||
const PointCloud::Point point = pointcloud->get_point(pidx - prim_offset);
|
||||
|
||||
point.bounds_grow(points, radius, bbox);
|
||||
|
||||
/* Motion points. */
|
||||
if (pointcloud->get_use_motion_blur()) {
|
||||
const Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
|
||||
if (attr_P->has_motion()) {
|
||||
const Attribute *attr_R = pointcloud->attributes.find(ATTR_STD_RADIUS);
|
||||
for (int attr_step = 1; attr_step < attr_P->num_motion_steps(); attr_step++) {
|
||||
const float3 P = attr_P->data<packed_float3>(attr_step)[point.index];
|
||||
const float r = attr_R->data<float>(attr_step)[point.index];
|
||||
bbox.grow(P, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Triangles. */
|
||||
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
|
||||
const int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
|
||||
const Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
|
||||
const packed_float3 *vpos = mesh->get_position();
|
||||
|
||||
triangle.bounds_grow(vpos, bbox);
|
||||
|
||||
/* Motion triangles. */
|
||||
if (mesh->use_motion_blur) {
|
||||
const Attribute *attr_P = mesh->attributes.find(ATTR_STD_POSITION);
|
||||
|
||||
if (attr_P->has_motion()) {
|
||||
for (int attr_step = 1; attr_step < attr_P->num_motion_steps(); attr_step++) {
|
||||
triangle.bounds_grow(attr_P->data<packed_float3>(attr_step), bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visibility |= ob->visibility_for_tracing();
|
||||
}
|
||||
}
|
||||
|
||||
/* Triangles */
|
||||
|
||||
void BVH2::pack_primitives()
|
||||
{
|
||||
const size_t tidx_size = pack.prim_index.size();
|
||||
/* Reserve size for arrays. */
|
||||
pack.prim_visibility.clear();
|
||||
pack.prim_visibility.resize(tidx_size);
|
||||
/* Fill in all the arrays. */
|
||||
for (unsigned int i = 0; i < tidx_size; i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
const int tob = pack.prim_object[i];
|
||||
Object *ob = objects[tob];
|
||||
pack.prim_visibility[i] = ob->visibility_for_tracing();
|
||||
}
|
||||
else {
|
||||
pack.prim_visibility[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack Instances */
|
||||
|
||||
void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
{
|
||||
/* Adjust primitive index to point to the triangle in the global array, for
|
||||
* geometry with transform applied and already in the top level BVH.
|
||||
*/
|
||||
for (size_t i = 0; i < pack.prim_index.size(); i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* track offsets of instanced BVH data in global array */
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
size_t nodes_offset = nodes_size;
|
||||
size_t nodes_leaf_offset = leaf_nodes_size;
|
||||
|
||||
/* clear array that gives the node indexes for instanced objects */
|
||||
pack.object_node.clear();
|
||||
|
||||
/* reserve */
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
|
||||
size_t pack_prim_index_offset = prim_index_size;
|
||||
size_t pack_nodes_offset = nodes_size;
|
||||
size_t pack_leaf_nodes_offset = leaf_nodes_size;
|
||||
size_t object_offset = 0;
|
||||
|
||||
for (Geometry *geom : geometry) {
|
||||
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh.get());
|
||||
|
||||
if (geom->need_build_bvh(params.bvh_layout)) {
|
||||
prim_index_size += bvh->pack.prim_index.size();
|
||||
nodes_size += bvh->pack.nodes.size();
|
||||
leaf_nodes_size += bvh->pack.leaf_nodes.size();
|
||||
}
|
||||
}
|
||||
|
||||
pack.prim_index.resize(prim_index_size);
|
||||
pack.prim_type.resize(prim_index_size);
|
||||
pack.prim_object.resize(prim_index_size);
|
||||
pack.prim_visibility.resize(prim_index_size);
|
||||
pack.nodes.resize(nodes_size);
|
||||
pack.leaf_nodes.resize(leaf_nodes_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0 ||
|
||||
params.num_motion_point_steps > 0)
|
||||
{
|
||||
pack.prim_time.resize(prim_index_size);
|
||||
}
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size()) ? pack.prim_index.data() : nullptr;
|
||||
int *pack_prim_type = (pack.prim_type.size()) ? pack.prim_type.data() : nullptr;
|
||||
int *pack_prim_object = (pack.prim_object.size()) ? pack.prim_object.data() : nullptr;
|
||||
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? pack.prim_visibility.data() :
|
||||
nullptr;
|
||||
int4 *pack_nodes = (pack.nodes.size()) ? pack.nodes.data() : nullptr;
|
||||
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? pack.leaf_nodes.data() : nullptr;
|
||||
float2 *pack_prim_time = (pack.prim_time.size()) ? pack.prim_time.data() : nullptr;
|
||||
|
||||
unordered_map<Geometry *, int> geometry_map;
|
||||
|
||||
/* merge */
|
||||
for (Object *ob : objects) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
/* We assume that if mesh doesn't need own BVH it was already included
|
||||
* into a top-level BVH and no packing here is needed.
|
||||
*/
|
||||
if (!geom->need_build_bvh(params.bvh_layout)) {
|
||||
pack.object_node[object_offset++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if mesh already added once, don't add it again, but used set
|
||||
* node offset for this object */
|
||||
const unordered_map<Geometry *, int>::iterator it = geometry_map.find(geom);
|
||||
|
||||
if (geometry_map.contains(geom)) {
|
||||
const int noffset = it->second;
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
continue;
|
||||
}
|
||||
|
||||
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh.get());
|
||||
|
||||
const int noffset = nodes_offset;
|
||||
const int noffset_leaf = nodes_leaf_offset;
|
||||
const int geom_prim_offset = geom->prim_offset;
|
||||
|
||||
/* fill in node indexes for instances */
|
||||
if (bvh->pack.root_index == -1) {
|
||||
pack.object_node[object_offset++] = -noffset_leaf - 1;
|
||||
}
|
||||
else {
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
}
|
||||
|
||||
geometry_map[geom] = pack.object_node[object_offset - 1];
|
||||
|
||||
/* merge primitive, object and triangle indexes */
|
||||
if (bvh->pack.prim_index.size()) {
|
||||
const size_t bvh_prim_index_size = bvh->pack.prim_index.size();
|
||||
int *bvh_prim_index = bvh->pack.prim_index.data();
|
||||
int *bvh_prim_type = bvh->pack.prim_type.data();
|
||||
uint *bvh_prim_visibility = bvh->pack.prim_visibility.data();
|
||||
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? bvh->pack.prim_time.data() : nullptr;
|
||||
|
||||
for (size_t i = 0; i < bvh_prim_index_size; i++) {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
|
||||
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
|
||||
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
|
||||
if (bvh_prim_time != nullptr) {
|
||||
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
|
||||
}
|
||||
pack_prim_index_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
/* merge nodes */
|
||||
if (bvh->pack.leaf_nodes.size()) {
|
||||
int4 *leaf_nodes_offset = bvh->pack.leaf_nodes.data();
|
||||
const size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
|
||||
for (size_t i = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE) {
|
||||
int4 data = leaf_nodes_offset[i];
|
||||
data.x += prim_offset;
|
||||
data.y += prim_offset;
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset] = data;
|
||||
for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) {
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
|
||||
}
|
||||
pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bvh->pack.nodes.size()) {
|
||||
int4 *bvh_nodes = bvh->pack.nodes.data();
|
||||
const size_t bvh_nodes_size = bvh->pack.nodes.size();
|
||||
|
||||
for (size_t i = 0; i < bvh_nodes_size;) {
|
||||
size_t nsize;
|
||||
size_t nsize_bbox;
|
||||
if (bvh_nodes[i].x & PATH_RAY_VISIBILITY_NODE_UNALIGNED) {
|
||||
nsize = BVH_UNALIGNED_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
else {
|
||||
nsize = BVH_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
|
||||
std::copy_n(bvh_nodes + i, nsize_bbox, pack_nodes + pack_nodes_offset);
|
||||
|
||||
/* Modify offsets into arrays */
|
||||
int4 data = bvh_nodes[i + nsize_bbox];
|
||||
data.z += (data.z < 0) ? -noffset_leaf : noffset;
|
||||
data.w += (data.w < 0) ? -noffset_leaf : noffset;
|
||||
pack_nodes[pack_nodes_offset + nsize_bbox] = data;
|
||||
|
||||
/* Usually this copies nothing, but we better
|
||||
* be prepared for possible node size extension.
|
||||
*/
|
||||
std::copy_n(&bvh_nodes[i + nsize_bbox + 1],
|
||||
(nsize - (nsize_bbox + 1)),
|
||||
&pack_nodes[pack_nodes_offset + nsize_bbox + 1]);
|
||||
|
||||
pack_nodes_offset += nsize;
|
||||
i += nsize;
|
||||
}
|
||||
}
|
||||
|
||||
nodes_offset += bvh->pack.nodes.size();
|
||||
nodes_leaf_offset += bvh->pack.leaf_nodes.size();
|
||||
prim_offset += bvh->pack.prim_index.size();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
97
blender-5.2.0/intern/cycles/bvh/bvh2.h
Normal file
97
blender-5.2.0/intern/cycles/bvh/bvh2.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "bvh/params.h"
|
||||
|
||||
#include "util/types.h"
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
// NOLINTBEGIN
|
||||
#define BVH_NODE_SIZE 4
|
||||
#define BVH_NODE_LEAF_SIZE 1
|
||||
#define BVH_UNALIGNED_NODE_SIZE 7
|
||||
// NOLINTEND
|
||||
|
||||
/* Pack Utility */
|
||||
struct BVHStackEntry {
|
||||
const BVHNode *node;
|
||||
int idx;
|
||||
|
||||
BVHStackEntry(const BVHNode *n = nullptr, const int i = 0);
|
||||
int encodeIdx() const;
|
||||
};
|
||||
|
||||
/* BVH2
|
||||
*
|
||||
* Typical BVH with each node having two children.
|
||||
*/
|
||||
class BVH2 : public BVH {
|
||||
public:
|
||||
BVH2(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
|
||||
void build(Progress &progress, Stats *stats);
|
||||
void refit(Progress &progress);
|
||||
|
||||
PackedBVH pack;
|
||||
|
||||
protected:
|
||||
/* Building process. */
|
||||
virtual unique_ptr<BVHNode> widen_children_nodes(unique_ptr<BVHNode> &&root);
|
||||
|
||||
/* pack */
|
||||
void pack_nodes(const BVHNode *root);
|
||||
|
||||
void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
|
||||
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1);
|
||||
|
||||
void pack_aligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1);
|
||||
void pack_aligned_node(const int idx,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1);
|
||||
|
||||
void pack_unaligned_inner(const BVHStackEntry &e,
|
||||
const BVHStackEntry &e0,
|
||||
const BVHStackEntry &e1);
|
||||
void pack_unaligned_node(const int idx,
|
||||
const Transform &aligned_space0,
|
||||
const Transform &aligned_space1,
|
||||
const BoundBox &b0,
|
||||
const BoundBox &b1,
|
||||
int c0,
|
||||
int c1,
|
||||
uint visibility0,
|
||||
uint visibility1);
|
||||
|
||||
/* refit */
|
||||
void refit_nodes();
|
||||
void refit_node(const int idx, bool leaf, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* Refit range of primitives. */
|
||||
void refit_primitives(const int start, const int end, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* triangles and strands */
|
||||
void pack_primitives();
|
||||
|
||||
/* merge instance BVH's */
|
||||
void pack_instances(const size_t nodes_size, const size_t leaf_nodes_size);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
765
blender-5.2.0/intern/cycles/bvh/embree.cpp
Normal file
765
blender-5.2.0/intern/cycles/bvh/embree.cpp
Normal file
@@ -0,0 +1,765 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* This class implements a ray accelerator for Cycles using Intel's Embree library.
|
||||
* It supports triangles, curves, object and deformation blur and instancing.
|
||||
*
|
||||
* Since Embree allows object to be either curves or triangles but not both, Cycles object IDs are
|
||||
* mapped to Embree IDs by multiplying by two and adding one for curves.
|
||||
*
|
||||
* This implementation shares RTCDevices between Cycles instances. Eventually each instance should
|
||||
* get a separate RTCDevice to correctly keep track of memory usage.
|
||||
*
|
||||
* Vertex and index buffers are duplicated between Cycles device arrays and Embree. These could be
|
||||
* merged, which would require changes to intersection refinement, shader setup, mesh light
|
||||
* sampling and a few other places in Cycles where direct access to vertex data is required.
|
||||
*/
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
|
||||
# include <embree4/rtcore_geometry.h>
|
||||
|
||||
# include "bvh/embree.h"
|
||||
|
||||
# include "scene/hair.h"
|
||||
# include "scene/mesh.h"
|
||||
# include "scene/object.h"
|
||||
# include "scene/pointcloud.h"
|
||||
|
||||
# include "util/log.h"
|
||||
# include "util/progress.h"
|
||||
# include "util/stats.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static_assert(Object::MAX_MOTION_STEPS <= RTC_MAX_TIME_STEP_COUNT,
|
||||
"Object and Embree max motion steps inconsistent");
|
||||
static_assert(Object::MAX_MOTION_STEPS == Geometry::MAX_MOTION_STEPS,
|
||||
"Object and Geometry max motion steps inconsistent");
|
||||
|
||||
static size_t unaccounted_mem = 0;
|
||||
|
||||
static bool rtc_memory_monitor_func(void *userPtr, const ssize_t bytes, const bool /*unused*/)
|
||||
{
|
||||
Stats *stats = (Stats *)userPtr;
|
||||
if (stats) {
|
||||
if (bytes > 0) {
|
||||
stats->mem_alloc(bytes);
|
||||
}
|
||||
else {
|
||||
stats->mem_free(-bytes);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* A stats pointer may not yet be available. Keep track of the memory usage for later. */
|
||||
if (bytes >= 0) {
|
||||
atomic_add_and_fetch_z(&unaccounted_mem, bytes);
|
||||
}
|
||||
else {
|
||||
atomic_sub_and_fetch_z(&unaccounted_mem, -bytes);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rtc_error_func(void * /*unused*/, enum RTCError /*unused*/, const char *str)
|
||||
{
|
||||
LOG_WARNING << str;
|
||||
}
|
||||
|
||||
static double progress_start_time = 0.0;
|
||||
|
||||
static bool rtc_progress_func(void *user_ptr, const double n)
|
||||
{
|
||||
Progress *progress = (Progress *)user_ptr;
|
||||
|
||||
if (time_dt() - progress_start_time < 0.25) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const string msg = string_printf("Building BVH %.0f%%", n * 100.0);
|
||||
progress->set_substatus(msg);
|
||||
progress_start_time = time_dt();
|
||||
|
||||
return !progress->get_cancel();
|
||||
}
|
||||
|
||||
BVHEmbree::BVHEmbree(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
scene(nullptr),
|
||||
rtc_device(nullptr),
|
||||
rtc_device_is_sycl(false),
|
||||
build_quality(RTC_BUILD_QUALITY_REFIT)
|
||||
{
|
||||
SIMD_SET_FLUSH_TO_ZERO;
|
||||
}
|
||||
|
||||
BVHEmbree::~BVHEmbree()
|
||||
{
|
||||
if (scene) {
|
||||
rtcReleaseScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::build(Progress &progress,
|
||||
Stats *stats,
|
||||
RTCDevice rtc_device_,
|
||||
const bool rtc_device_is_sycl_)
|
||||
{
|
||||
rtc_device = rtc_device_;
|
||||
rtc_device_is_sycl = rtc_device_is_sycl_;
|
||||
assert(rtc_device);
|
||||
|
||||
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, nullptr);
|
||||
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
|
||||
|
||||
progress.set_substatus("Building BVH");
|
||||
|
||||
if (scene) {
|
||||
rtcReleaseScene(scene);
|
||||
scene = nullptr;
|
||||
}
|
||||
|
||||
const bool dynamic = params.bvh_type == BVH_TYPE_DYNAMIC;
|
||||
const bool compact = params.use_compact_structure;
|
||||
|
||||
scene = rtcNewScene(rtc_device);
|
||||
const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) |
|
||||
(compact ? RTC_SCENE_FLAG_COMPACT : RTC_SCENE_FLAG_NONE) |
|
||||
RTC_SCENE_FLAG_ROBUST |
|
||||
RTC_SCENE_FLAG_FILTER_FUNCTION_IN_ARGUMENTS;
|
||||
rtcSetSceneFlags(scene, scene_flags);
|
||||
build_quality = dynamic ? RTC_BUILD_QUALITY_LOW :
|
||||
(params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH :
|
||||
RTC_BUILD_QUALITY_MEDIUM);
|
||||
if (build_quality == RTC_BUILD_QUALITY_HIGH && rtc_device_is_sycl) {
|
||||
/* To work around a known issue in the Intel GPU driver regarding the High
|
||||
* quality BVH build option, we reduce it to Medium. There is no impact on
|
||||
* render quality from this change. There is a small expected performance
|
||||
* impact on intersection speed and BVH building speed, but this is
|
||||
* unavoidable at the moment - using High quality leads to crashes, so we
|
||||
* have no choice. This workaround can be removed once the fix appears in
|
||||
* public drivers and we raise the minimum oneAPI backend driver version
|
||||
* accordingly. */
|
||||
/* This workaround is applied only to GPU, per Sergey. See #158123. */
|
||||
LOG_INFO
|
||||
<< "Due to a known issue in Intel GPU drivers, overriding RTC_BUILD_QUALITY_HIGH to "
|
||||
"RTC_BUILD_QUALITY_MEDIUM to prevent crashes. This workaround will be removed only "
|
||||
"in a future Blender release.";
|
||||
build_quality = RTC_BUILD_QUALITY_MEDIUM;
|
||||
}
|
||||
rtcSetSceneBuildQuality(scene, build_quality);
|
||||
|
||||
int i = 0;
|
||||
for (Object *ob : objects) {
|
||||
if (params.top_level) {
|
||||
if (!ob->is_traceable()) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (!ob->get_geometry()->is_instanced()) {
|
||||
add_object(ob, i);
|
||||
}
|
||||
else {
|
||||
add_instance(ob, i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
add_object(ob, i);
|
||||
}
|
||||
++i;
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
|
||||
rtcCommitScene(scene);
|
||||
}
|
||||
|
||||
const char *BVHEmbree::get_error_string(RTCError error_code)
|
||||
{
|
||||
# if RTC_VERSION >= 40303
|
||||
return rtcGetErrorString(error_code);
|
||||
# else
|
||||
switch (error_code) {
|
||||
case RTC_ERROR_NONE:
|
||||
return "no error";
|
||||
case RTC_ERROR_UNKNOWN:
|
||||
return "unknown error";
|
||||
case RTC_ERROR_INVALID_ARGUMENT:
|
||||
return "invalid argument error";
|
||||
case RTC_ERROR_INVALID_OPERATION:
|
||||
return "invalid operation error";
|
||||
case RTC_ERROR_OUT_OF_MEMORY:
|
||||
return "out of memory error";
|
||||
case RTC_ERROR_UNSUPPORTED_CPU:
|
||||
return "unsupported cpu error";
|
||||
case RTC_ERROR_CANCELLED:
|
||||
return "cancelled";
|
||||
default:
|
||||
/* We should never end here unless enum for RTC errors would change. */
|
||||
return "unknown error";
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(WITH_EMBREE_GPU) && RTC_VERSION >= 40302
|
||||
/* offload_scenes_to_gpu() uses rtcGetDeviceError() which also resets Embree error status,
|
||||
* we propagate its value so it doesn't get lost. */
|
||||
RTCError BVHEmbree::offload_scenes_to_gpu(const vector<RTCScene> &scenes)
|
||||
{
|
||||
/* Having BVH on GPU is more performance-critical than texture data.
|
||||
* In order to ensure good performance even when running out of GPU
|
||||
* memory, we force BVH to migrate to GPU before allocating other textures
|
||||
* that may not fit. */
|
||||
for (const RTCScene &embree_scene : scenes) {
|
||||
RTCSceneFlags scene_flags = rtcGetSceneFlags(embree_scene);
|
||||
scene_flags = scene_flags | RTC_SCENE_FLAG_PREFETCH_USM_SHARED_ON_GPU;
|
||||
rtcSetSceneFlags(embree_scene, scene_flags);
|
||||
rtcCommitScene(embree_scene);
|
||||
/* In case of any errors from Embree, we should stop
|
||||
* the execution and propagate the error. */
|
||||
RTCError error_code = rtcGetDeviceError(rtc_device);
|
||||
if (error_code != RTC_ERROR_NONE) {
|
||||
return error_code;
|
||||
}
|
||||
}
|
||||
return RTC_ERROR_NONE;
|
||||
}
|
||||
# endif
|
||||
|
||||
void BVHEmbree::add_object(Object *ob, const int i)
|
||||
{
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
add_triangles(ob, mesh, i);
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
if (hair->is_traceable()) {
|
||||
add_curves(ob, hair, i);
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
if (pointcloud->num_points() > 0) {
|
||||
add_points(ob, pointcloud, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::add_instance(Object *ob, const int i)
|
||||
{
|
||||
BVHEmbree *instance_bvh = static_cast<BVHEmbree *>(ob->get_geometry()->bvh.get());
|
||||
assert(instance_bvh != nullptr);
|
||||
|
||||
const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1;
|
||||
const size_t num_motion_steps = min(num_object_motion_steps, (size_t)RTC_MAX_TIME_STEP_COUNT);
|
||||
assert(num_object_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_INSTANCE);
|
||||
rtcSetGeometryInstancedScene(geom_id, instance_bvh->scene);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
if (ob->use_motion()) {
|
||||
array<DecomposedTransform> decomp(ob->get_motion().size());
|
||||
transform_motion_decompose(decomp.data(), ob->get_motion().data(), ob->get_motion().size());
|
||||
for (size_t step = 0; step < num_motion_steps; ++step) {
|
||||
RTCQuaternionDecomposition rtc_decomp;
|
||||
rtcInitQuaternionDecomposition(&rtc_decomp);
|
||||
rtcQuaternionDecompositionSetQuaternion(
|
||||
&rtc_decomp, decomp[step].x.w, decomp[step].x.x, decomp[step].x.y, decomp[step].x.z);
|
||||
rtcQuaternionDecompositionSetScale(
|
||||
&rtc_decomp, decomp[step].y.w, decomp[step].z.w, decomp[step].w.w);
|
||||
rtcQuaternionDecompositionSetTranslation(
|
||||
&rtc_decomp, decomp[step].y.x, decomp[step].y.y, decomp[step].y.z);
|
||||
rtcQuaternionDecompositionSetSkew(
|
||||
&rtc_decomp, decomp[step].z.x, decomp[step].z.y, decomp[step].w.x);
|
||||
rtcSetGeometryTransformQuaternion(geom_id, step, &rtc_decomp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
rtcSetGeometryTransform(
|
||||
geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm());
|
||||
}
|
||||
|
||||
rtcSetGeometryUserData(geom_id,
|
||||
# if RTC_VERSION >= 40400
|
||||
(void *)rtcGetSceneTraversable(instance_bvh->scene)
|
||||
# else
|
||||
(void *)instance_bvh->scene
|
||||
# endif
|
||||
);
|
||||
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, const int i)
|
||||
{
|
||||
const size_t prim_offset = mesh->prim_offset;
|
||||
|
||||
const Attribute *attr_P = nullptr;
|
||||
size_t num_motion_steps = 1;
|
||||
if (mesh->has_motion_blur()) {
|
||||
attr_P = mesh->attributes.find(ATTR_STD_POSITION);
|
||||
if (attr_P->has_motion()) {
|
||||
num_motion_steps = mesh->get_motion_steps();
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
num_motion_steps = min(num_motion_steps, (size_t)RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
const size_t num_triangles = mesh->num_triangles();
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE);
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
const int *triangles = mesh->get_triangles().data();
|
||||
if (!rtc_device_is_sycl) {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT3,
|
||||
triangles,
|
||||
0,
|
||||
sizeof(int) * 3,
|
||||
num_triangles);
|
||||
}
|
||||
else {
|
||||
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
|
||||
* happen on GPU, and we cannot use standard host pointers at this point. So instead
|
||||
* of making a shared geometry buffer - a new Embree buffer will be created and data
|
||||
* will be copied. */
|
||||
int *triangles_buffer = nullptr;
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
triangles_buffer = (int *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT3,
|
||||
sizeof(int) * 3,
|
||||
num_triangles
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&triangles_buffer),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
assert(triangles_buffer);
|
||||
if (triangles_buffer) {
|
||||
static_assert(sizeof(int) == sizeof(uint));
|
||||
std::memcpy(triangles_buffer, triangles, sizeof(int) * 3 * (num_triangles));
|
||||
}
|
||||
}
|
||||
set_tri_vertex_buffer(geom_id, mesh, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update)
|
||||
{
|
||||
const Attribute *attr_P = mesh->attributes.find(ATTR_STD_POSITION);
|
||||
size_t num_motion_steps = 1;
|
||||
if (mesh->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = mesh->get_motion_steps();
|
||||
if (num_motion_steps > RTC_MAX_TIME_STEP_COUNT) {
|
||||
assert(0);
|
||||
num_motion_steps = RTC_MAX_TIME_STEP_COUNT;
|
||||
}
|
||||
}
|
||||
const size_t num_verts = mesh->num_verts();
|
||||
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
const packed_float3 *verts = attr_P->data_at_time_step<packed_float3>(t, num_motion_steps);
|
||||
|
||||
if (update) {
|
||||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
if (!rtc_device_is_sycl) {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
verts,
|
||||
0,
|
||||
sizeof(packed_float3),
|
||||
num_verts);
|
||||
}
|
||||
else {
|
||||
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
|
||||
* happen on GPU, and we cannot use standard host pointers at this point. So instead
|
||||
* of making a shared geometry buffer - a new Embree buffer will be created and data
|
||||
* will be copied. */
|
||||
/* As float3 is packed on GPU side, we map it to packed_float3. */
|
||||
/* There is no need for additional padding in rtcSetNewGeometryBuffer since Embree 3.6:
|
||||
* "Fixed automatic vertex buffer padding when using rtcSetNewGeometry API function". */
|
||||
packed_float3 *verts_buffer = nullptr;
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
verts_buffer = (packed_float3 *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
sizeof(packed_float3),
|
||||
num_verts
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&verts_buffer),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
assert(verts_buffer);
|
||||
if (verts_buffer) {
|
||||
for (size_t i = (size_t)0; i < num_verts; ++i) {
|
||||
verts_buffer[i].x = verts[i].x;
|
||||
verts_buffer[i].y = verts[i].y;
|
||||
verts_buffer[i].z = verts[i].z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs the hair motion curve data control variables (CVs) into float4s as [x y z radius]
|
||||
*/
|
||||
template<typename T>
|
||||
void pack_motion_verts(const size_t num_curves,
|
||||
const Hair *hair,
|
||||
const T *verts,
|
||||
const float *curve_radius,
|
||||
float4 *rtc_verts,
|
||||
CurveShapeType curve_shape)
|
||||
{
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
int fk = c.first_key;
|
||||
|
||||
if (curve_shape == CURVE_THICK_LINEAR) {
|
||||
for (int k = 0; k < c.num_keys; ++k, ++fk) {
|
||||
rtc_verts[k].x = verts[fk].x;
|
||||
rtc_verts[k].y = verts[fk].y;
|
||||
rtc_verts[k].z = verts[fk].z;
|
||||
rtc_verts[k].w = curve_radius[fk];
|
||||
}
|
||||
rtc_verts += c.num_keys;
|
||||
}
|
||||
else {
|
||||
for (int k = 1; k < c.num_keys + 1; ++k, ++fk) {
|
||||
rtc_verts[k].x = verts[fk].x;
|
||||
rtc_verts[k].y = verts[fk].y;
|
||||
rtc_verts[k].z = verts[fk].z;
|
||||
rtc_verts[k].w = curve_radius[fk];
|
||||
}
|
||||
/* Duplicate Embree's Catmull-Rom spline CVs at the start and end of each curve. */
|
||||
rtc_verts[0] = rtc_verts[1];
|
||||
rtc_verts[c.num_keys + 1] = rtc_verts[c.num_keys];
|
||||
rtc_verts += c.num_keys + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update)
|
||||
{
|
||||
const Attribute *attr_P = hair->attributes.find(ATTR_STD_POSITION);
|
||||
const Attribute *attr_R = hair->attributes.find(ATTR_STD_RADIUS);
|
||||
size_t num_motion_steps = 1;
|
||||
if (hair->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = hair->get_motion_steps();
|
||||
}
|
||||
|
||||
const size_t num_curves = hair->num_curves();
|
||||
size_t num_keys = 0;
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
num_keys += c.num_keys;
|
||||
}
|
||||
|
||||
/* Catmull-Rom splines need extra CVs at the beginning and end of each curve. */
|
||||
size_t num_keys_embree = num_keys;
|
||||
num_keys_embree += num_curves * 2;
|
||||
|
||||
/* Copy the CV data to Embree */
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
float4 *rtc_verts = nullptr;
|
||||
if (update) {
|
||||
rtc_verts = (float4 *)rtcGetGeometryBufferData(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
rtc_verts = (float4 *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT4,
|
||||
sizeof(float) * 4,
|
||||
num_keys_embree
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&rtc_verts),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
}
|
||||
|
||||
assert(rtc_verts);
|
||||
if (rtc_verts) {
|
||||
const size_t num_curves = hair->num_curves();
|
||||
pack_motion_verts<packed_float3>(
|
||||
num_curves,
|
||||
hair,
|
||||
attr_P->data_at_time_step<packed_float3>(t, num_motion_steps),
|
||||
attr_R->data_at_time_step<float>(t, num_motion_steps),
|
||||
rtc_verts,
|
||||
hair->curve_shape);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::set_point_vertex_buffer(RTCGeometry geom_id,
|
||||
const PointCloud *pointcloud,
|
||||
const bool update)
|
||||
{
|
||||
const Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
const Attribute *attr_R = pointcloud->attributes.find(ATTR_STD_RADIUS);
|
||||
size_t num_motion_steps = 1;
|
||||
if (pointcloud->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = pointcloud->get_motion_steps();
|
||||
}
|
||||
|
||||
const size_t num_points = pointcloud->num_points();
|
||||
|
||||
/* Copy the point data to Embree. */
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
|
||||
float4 *rtc_verts = nullptr;
|
||||
if (update) {
|
||||
rtc_verts = (float4 *)rtcGetGeometryBufferData(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
rtc_verts = (float4 *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT4,
|
||||
sizeof(float) * 4,
|
||||
num_points
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&rtc_verts),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
}
|
||||
|
||||
assert(rtc_verts);
|
||||
if (rtc_verts) {
|
||||
const packed_float3 *verts = attr_P->data_at_time_step<packed_float3>(t, num_motion_steps);
|
||||
const float *radius = attr_R->data_at_time_step<float>(t, num_motion_steps);
|
||||
for (size_t j = 0; j < num_points; ++j) {
|
||||
rtc_verts[j] = make_float4(float3(verts[j]), radius[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::add_points(const Object *ob, const PointCloud *pointcloud, const int i)
|
||||
{
|
||||
const size_t prim_offset = pointcloud->prim_offset;
|
||||
|
||||
size_t num_motion_steps = 1;
|
||||
if (pointcloud->has_motion_blur()) {
|
||||
const Attribute *attr_P = pointcloud->attributes.find(ATTR_STD_POSITION);
|
||||
if (attr_P->has_motion()) {
|
||||
num_motion_steps = pointcloud->get_motion_steps();
|
||||
}
|
||||
}
|
||||
|
||||
const enum RTCGeometryType type = RTC_GEOMETRY_TYPE_SPHERE_POINT;
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
|
||||
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
set_point_vertex_buffer(geom_id, pointcloud, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, const int i)
|
||||
{
|
||||
const size_t prim_offset = hair->curve_segment_offset;
|
||||
|
||||
const Attribute *attr_P = hair->attributes.find(ATTR_STD_POSITION);
|
||||
size_t num_motion_steps = 1;
|
||||
if (hair->has_motion_blur() && attr_P->has_motion()) {
|
||||
num_motion_steps = hair->get_motion_steps();
|
||||
}
|
||||
|
||||
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
num_motion_steps = min(num_motion_steps, (size_t)RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
const size_t num_curves = hair->num_curves();
|
||||
size_t num_segments = 0;
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
assert(c.num_segments() > 0);
|
||||
num_segments += c.num_segments();
|
||||
}
|
||||
|
||||
const enum RTCGeometryType type = (hair->curve_shape == CURVE_THICK_LINEAR ?
|
||||
RTC_GEOMETRY_TYPE_ROUND_LINEAR_CURVE :
|
||||
hair->curve_shape == CURVE_RIBBON ?
|
||||
RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
|
||||
RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
|
||||
rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1);
|
||||
unsigned *rtc_indices = nullptr;
|
||||
# if RTC_VERSION >= 40400
|
||||
rtcSetNewGeometryBufferHostDevice(
|
||||
# else
|
||||
rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
|
||||
# endif
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT,
|
||||
sizeof(int),
|
||||
num_segments
|
||||
# if RTC_VERSION >= 40400
|
||||
,
|
||||
(void **)(&rtc_indices),
|
||||
nullptr
|
||||
# endif
|
||||
);
|
||||
|
||||
size_t rtc_index = 0;
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve c = hair->get_curve(j);
|
||||
for (size_t k = 0; k < c.num_segments(); ++k) {
|
||||
rtc_indices[rtc_index] = c.first_key + k;
|
||||
if (hair->curve_shape != CURVE_THICK_LINEAR) {
|
||||
/* Room for extra CVs at Catmull-Rom splines. */
|
||||
rtc_indices[rtc_index] += j * 2;
|
||||
}
|
||||
|
||||
++rtc_index;
|
||||
}
|
||||
}
|
||||
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
set_curve_vertex_buffer(geom_id, hair, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
rtcSetGeometryEnableFilterFunctionFromArguments(geom_id, true);
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
rtcAttachGeometryByID(scene, geom_id, i * 2 + 1);
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::refit(Progress &progress)
|
||||
{
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
|
||||
/* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */
|
||||
unsigned geom_id = 0;
|
||||
for (Object *ob : objects) {
|
||||
if (!params.top_level || (ob->is_traceable() && !ob->get_geometry()->is_instanced())) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id);
|
||||
set_tri_vertex_buffer(geom, mesh, true);
|
||||
rtcSetGeometryUserData(geom, (void *)mesh->prim_offset);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
if (hair->is_traceable()) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id + 1);
|
||||
set_curve_vertex_buffer(geom, hair, true);
|
||||
rtcSetGeometryUserData(geom, (void *)hair->curve_segment_offset);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
if (pointcloud->num_points() > 0) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id);
|
||||
set_point_vertex_buffer(geom, pointcloud, true);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
}
|
||||
geom_id += 2;
|
||||
}
|
||||
|
||||
rtcCommitScene(scene);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_EMBREE */
|
||||
65
blender-5.2.0/intern/cycles/bvh/embree.h
Normal file
65
blender-5.2.0/intern/cycles/bvh/embree.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_EMBREE
|
||||
|
||||
# include <embree4/rtcore.h>
|
||||
# include <embree4/rtcore_scene.h>
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
# include "bvh/params.h"
|
||||
|
||||
# include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Hair;
|
||||
class Mesh;
|
||||
class PointCloud;
|
||||
|
||||
class BVHEmbree : public BVH {
|
||||
public:
|
||||
void build(Progress &progress,
|
||||
Stats *stats,
|
||||
RTCDevice rtc_device,
|
||||
const bool rtc_device_is_sycl_ = false);
|
||||
void refit(Progress &progress);
|
||||
|
||||
# if defined(WITH_EMBREE_GPU) && RTC_VERSION >= 40302
|
||||
RTCError offload_scenes_to_gpu(const vector<RTCScene> &scenes);
|
||||
# endif
|
||||
|
||||
const char *get_error_string(RTCError error_code);
|
||||
|
||||
RTCScene scene;
|
||||
|
||||
BVHEmbree(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
~BVHEmbree() override;
|
||||
|
||||
protected:
|
||||
void add_object(Object *ob, const int i);
|
||||
void add_instance(Object *ob, const int i);
|
||||
void add_curves(const Object *ob, const Hair *hair, const int i);
|
||||
void add_points(const Object *ob, const PointCloud *pointcloud, const int i);
|
||||
void add_triangles(const Object *ob, const Mesh *mesh, const int i);
|
||||
|
||||
private:
|
||||
void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update);
|
||||
void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update);
|
||||
void set_point_vertex_buffer(RTCGeometry geom_id,
|
||||
const PointCloud *pointcloud,
|
||||
const bool update);
|
||||
|
||||
RTCDevice rtc_device;
|
||||
bool rtc_device_is_sycl;
|
||||
enum RTCBuildQuality build_quality;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_EMBREE */
|
||||
42
blender-5.2.0/intern/cycles/bvh/hiprt.cpp
Normal file
42
blender-5.2.0/intern/cycles/bvh/hiprt.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_HIPRT
|
||||
|
||||
# include "bvh/hiprt.h"
|
||||
|
||||
# include "scene/mesh.h"
|
||||
# include "scene/object.h"
|
||||
|
||||
# include "device/hiprt/device_impl.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHHIPRT::BVHHIPRT(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *in_device)
|
||||
: BVH(params, geometry, objects),
|
||||
hiprt_geom(nullptr),
|
||||
custom_primitive_bound(in_device, "Custom Primitive Bound", MEM_READ_ONLY),
|
||||
triangle_index(in_device, "HIPRT Triangle Index", MEM_READ_ONLY),
|
||||
vertex_data(in_device, "vertex_data", MEM_READ_ONLY),
|
||||
aabb_overlap_ratio(0.0f),
|
||||
device(in_device)
|
||||
{
|
||||
triangle_mesh = {nullptr};
|
||||
custom_prim_aabb = {nullptr};
|
||||
}
|
||||
|
||||
BVHHIPRT::~BVHHIPRT()
|
||||
{
|
||||
custom_primitive_bound.free();
|
||||
triangle_index.free();
|
||||
vertex_data.free();
|
||||
device->release_bvh(this);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
54
blender-5.2.0/intern/cycles/bvh/hiprt.h
Normal file
54
blender-5.2.0/intern/cycles/bvh/hiprt.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_HIPRT
|
||||
|
||||
# pragma once
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
# include "bvh/params.h"
|
||||
# include "device/memory.h"
|
||||
|
||||
# include <hiprt/hiprt_types.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHHIPRT : public BVH {
|
||||
public:
|
||||
friend class HIPDevice;
|
||||
|
||||
bool is_tlas()
|
||||
{
|
||||
return params.top_level;
|
||||
}
|
||||
|
||||
hiprtGeometry hiprt_geom;
|
||||
hiprtTriangleMeshPrimitive triangle_mesh;
|
||||
hiprtAABBListPrimitive custom_prim_aabb;
|
||||
hiprtGeometryBuildInput geom_input;
|
||||
|
||||
vector<int2> custom_prim_info; /* x: prim_id, y: prim_type */
|
||||
vector<float2> prims_time;
|
||||
|
||||
/* Custom primitives. */
|
||||
device_vector<BoundBox> custom_primitive_bound;
|
||||
device_vector<int> triangle_index;
|
||||
device_vector<float> vertex_data;
|
||||
|
||||
float aabb_overlap_ratio;
|
||||
|
||||
BVHHIPRT(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *in_device);
|
||||
|
||||
~BVHHIPRT() override;
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
22
blender-5.2.0/intern/cycles/bvh/metal.h
Normal file
22
blender-5.2.0/intern/cycles/bvh/metal.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_METAL
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
|
||||
# include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
unique_ptr<BVH> bvh_metal_create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_METAL */
|
||||
23
blender-5.2.0/intern/cycles/bvh/metal.mm
Normal file
23
blender-5.2.0/intern/cycles/bvh/metal.mm
Normal file
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_METAL
|
||||
|
||||
# include "device/metal/bvh.h"
|
||||
|
||||
# include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
unique_ptr<BVH> bvh_metal_create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device)
|
||||
{
|
||||
return make_unique<BVHMetal>(params, geometry, objects, device);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_METAL */
|
||||
24
blender-5.2.0/intern/cycles/bvh/multi.cpp
Normal file
24
blender-5.2.0/intern/cycles/bvh/multi.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "bvh/multi.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHMulti::BVHMulti(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_)
|
||||
{
|
||||
}
|
||||
|
||||
void BVHMulti::replace_geometry(const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects)
|
||||
{
|
||||
for (unique_ptr<BVH> &bvh : sub_bvhs) {
|
||||
bvh->replace_geometry(geometry, objects);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
28
blender-5.2.0/intern/cycles/bvh/multi.h
Normal file
28
blender-5.2.0/intern/cycles/bvh/multi.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "bvh/params.h"
|
||||
|
||||
#include <util/unique_ptr.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHMulti : public BVH {
|
||||
public:
|
||||
vector<unique_ptr<BVH>> sub_bvhs;
|
||||
|
||||
BVHMulti(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
|
||||
protected:
|
||||
void replace_geometry(const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects) override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
211
blender-5.2.0/intern/cycles/bvh/node.cpp
Normal file
211
blender-5.2.0/intern/cycles/bvh/node.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/node.h"
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* BVH Node */
|
||||
|
||||
int BVHNode::getSubtreeSize(BVH_STAT stat) const
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
switch (stat) {
|
||||
case BVH_STAT_NODE_COUNT:
|
||||
cnt = 1;
|
||||
break;
|
||||
case BVH_STAT_LEAF_COUNT:
|
||||
cnt = is_leaf() ? 1 : 0;
|
||||
break;
|
||||
case BVH_STAT_INNER_COUNT:
|
||||
cnt = is_leaf() ? 0 : 1;
|
||||
break;
|
||||
case BVH_STAT_TRIANGLE_COUNT:
|
||||
cnt = is_leaf() ? reinterpret_cast<const LeafNode *>(this)->num_triangles() : 0;
|
||||
break;
|
||||
case BVH_STAT_CHILDNODE_COUNT:
|
||||
cnt = num_children();
|
||||
break;
|
||||
case BVH_STAT_ALIGNED_COUNT:
|
||||
if (!is_unaligned) {
|
||||
cnt = 1;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_UNALIGNED_COUNT:
|
||||
if (is_unaligned) {
|
||||
cnt = 1;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_ALIGNED_INNER_COUNT:
|
||||
if (!is_leaf()) {
|
||||
bool has_unaligned = false;
|
||||
for (int j = 0; j < num_children(); j++) {
|
||||
has_unaligned |= get_child(j)->is_unaligned;
|
||||
}
|
||||
cnt += has_unaligned ? 0 : 1;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_UNALIGNED_INNER_COUNT:
|
||||
if (!is_leaf()) {
|
||||
bool has_unaligned = false;
|
||||
for (int j = 0; j < num_children(); j++) {
|
||||
has_unaligned |= get_child(j)->is_unaligned;
|
||||
}
|
||||
cnt += has_unaligned ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
case BVH_STAT_ALIGNED_LEAF_COUNT:
|
||||
cnt = (is_leaf() && !is_unaligned) ? 1 : 0;
|
||||
break;
|
||||
case BVH_STAT_UNALIGNED_LEAF_COUNT:
|
||||
cnt = (is_leaf() && is_unaligned) ? 1 : 0;
|
||||
break;
|
||||
case BVH_STAT_DEPTH:
|
||||
if (is_leaf()) {
|
||||
cnt = 1;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < num_children(); i++) {
|
||||
cnt = max(cnt, get_child(i)->getSubtreeSize(stat));
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
return cnt;
|
||||
default:
|
||||
assert(0); /* unknown mode */
|
||||
}
|
||||
|
||||
if (!is_leaf()) {
|
||||
for (int i = 0; i < num_children(); i++) {
|
||||
cnt += get_child(i)->getSubtreeSize(stat);
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
float BVHNode::computeSubtreeSAHCost(const BVHParams &p, const float probability) const
|
||||
{
|
||||
float SAH = probability * p.cost(num_children(), num_triangles());
|
||||
|
||||
for (int i = 0; i < num_children(); i++) {
|
||||
BVHNode *child = get_child(i);
|
||||
SAH += child->computeSubtreeSAHCost(
|
||||
p, probability * child->bounds.safe_area() / bounds.safe_area());
|
||||
}
|
||||
|
||||
return SAH;
|
||||
}
|
||||
|
||||
uint BVHNode::update_visibility()
|
||||
{
|
||||
if (!is_leaf() && visibility == 0) {
|
||||
InnerNode *inner = (InnerNode *)this;
|
||||
BVHNode *child0 = inner->children[0].get();
|
||||
BVHNode *child1 = inner->children[1].get();
|
||||
|
||||
visibility = child0->update_visibility() | child1->update_visibility();
|
||||
}
|
||||
|
||||
return visibility;
|
||||
}
|
||||
|
||||
void BVHNode::update_time()
|
||||
{
|
||||
if (!is_leaf()) {
|
||||
InnerNode *inner = (InnerNode *)this;
|
||||
BVHNode *child0 = inner->children[0].get();
|
||||
BVHNode *child1 = inner->children[1].get();
|
||||
child0->update_time();
|
||||
child1->update_time();
|
||||
time_from = min(child0->time_from, child1->time_from);
|
||||
time_to = max(child0->time_to, child1->time_to);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct DumpTraversalContext {
|
||||
/* Descriptor of while where writing is happening. */
|
||||
FILE *stream;
|
||||
/* Unique identifier of the node current. */
|
||||
int id;
|
||||
};
|
||||
|
||||
void dump_subtree(DumpTraversalContext *context,
|
||||
const BVHNode *node,
|
||||
const BVHNode *parent = nullptr)
|
||||
{
|
||||
if (node->is_leaf()) {
|
||||
fprintf(context->stream,
|
||||
" node_%p [label=\"%d\",fillcolor=\"#ccccee\",style=filled]\n",
|
||||
node,
|
||||
context->id);
|
||||
}
|
||||
else {
|
||||
fprintf(context->stream,
|
||||
" node_%p [label=\"%d\",fillcolor=\"#cceecc\",style=filled]\n",
|
||||
node,
|
||||
context->id);
|
||||
}
|
||||
if (parent != nullptr) {
|
||||
fprintf(context->stream, " node_%p -> node_%p;\n", parent, node);
|
||||
}
|
||||
context->id += 1;
|
||||
for (int i = 0; i < node->num_children(); ++i) {
|
||||
dump_subtree(context, node->get_child(i), node);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void BVHNode::dump_graph(const char *filename)
|
||||
{
|
||||
DumpTraversalContext context;
|
||||
context.stream = fopen(filename, "w");
|
||||
if (context.stream == nullptr) {
|
||||
return;
|
||||
}
|
||||
context.id = 0;
|
||||
fprintf(context.stream, "digraph BVH {\n");
|
||||
dump_subtree(&context, this);
|
||||
fprintf(context.stream, "}\n");
|
||||
fclose(context.stream);
|
||||
}
|
||||
|
||||
/* Inner Node */
|
||||
|
||||
void InnerNode::print(const int depth) const
|
||||
{
|
||||
for (int i = 0; i < depth; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf("inner node %p\n", (void *)this);
|
||||
|
||||
if (children[0]) {
|
||||
children[0]->print(depth + 1);
|
||||
}
|
||||
if (children[1]) {
|
||||
children[1]->print(depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void LeafNode::print(const int depth) const
|
||||
{
|
||||
for (int i = 0; i < depth; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf("leaf node %d to %d\n", lo, hi);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
202
blender-5.2.0/intern/cycles/bvh/node.h
Normal file
202
blender-5.2.0/intern/cycles/bvh/node.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/types.h"
|
||||
#include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
enum BVH_STAT {
|
||||
BVH_STAT_NODE_COUNT,
|
||||
BVH_STAT_INNER_COUNT,
|
||||
BVH_STAT_LEAF_COUNT,
|
||||
BVH_STAT_TRIANGLE_COUNT,
|
||||
BVH_STAT_CHILDNODE_COUNT,
|
||||
BVH_STAT_ALIGNED_COUNT,
|
||||
BVH_STAT_UNALIGNED_COUNT,
|
||||
BVH_STAT_ALIGNED_INNER_COUNT,
|
||||
BVH_STAT_UNALIGNED_INNER_COUNT,
|
||||
BVH_STAT_ALIGNED_LEAF_COUNT,
|
||||
BVH_STAT_UNALIGNED_LEAF_COUNT,
|
||||
BVH_STAT_DEPTH,
|
||||
};
|
||||
|
||||
class BVHParams;
|
||||
|
||||
class BVHNode {
|
||||
public:
|
||||
virtual ~BVHNode() = default;
|
||||
|
||||
virtual bool is_leaf() const = 0;
|
||||
virtual int num_children() const = 0;
|
||||
virtual BVHNode *get_child(const int i) const = 0;
|
||||
virtual int num_triangles() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual void print(const int depth = 0) const = 0;
|
||||
|
||||
void set_aligned_space(const Transform &aligned_space)
|
||||
{
|
||||
is_unaligned = true;
|
||||
if (this->aligned_space == nullptr) {
|
||||
this->aligned_space = make_unique<Transform>(aligned_space);
|
||||
}
|
||||
else {
|
||||
*this->aligned_space = aligned_space;
|
||||
}
|
||||
}
|
||||
|
||||
Transform get_aligned_space() const
|
||||
{
|
||||
if (aligned_space == nullptr) {
|
||||
return transform_identity();
|
||||
}
|
||||
return *aligned_space;
|
||||
}
|
||||
|
||||
bool has_unaligned() const
|
||||
{
|
||||
if (is_leaf()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < num_children(); ++i) {
|
||||
if (get_child(i)->is_unaligned) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subtree functions
|
||||
int getSubtreeSize(BVH_STAT stat = BVH_STAT_NODE_COUNT) const;
|
||||
float computeSubtreeSAHCost(const BVHParams &p, const float probability = 1.0f) const;
|
||||
|
||||
uint update_visibility();
|
||||
void update_time();
|
||||
|
||||
/* Dump the content of the tree as a graphviz file. */
|
||||
void dump_graph(const char *filename);
|
||||
|
||||
// Properties.
|
||||
BoundBox bounds;
|
||||
uint visibility = 0;
|
||||
|
||||
bool is_unaligned = false;
|
||||
|
||||
/* TODO(sergey): Can be stored as 3x3 matrix, but better to have some
|
||||
* utilities and type defines in util_transform first.
|
||||
*/
|
||||
unique_ptr<Transform> aligned_space;
|
||||
|
||||
float time_from = 0.0f, time_to = 1.0f;
|
||||
|
||||
protected:
|
||||
explicit BVHNode(const BoundBox &bounds) : bounds(bounds) {}
|
||||
|
||||
explicit BVHNode(const BVHNode &other)
|
||||
: bounds(other.bounds),
|
||||
visibility(other.visibility),
|
||||
is_unaligned(other.is_unaligned),
|
||||
|
||||
time_from(other.time_from),
|
||||
time_to(other.time_to)
|
||||
{
|
||||
if (other.aligned_space != nullptr) {
|
||||
assert(other.is_unaligned);
|
||||
aligned_space = make_unique<Transform>(*other.aligned_space);
|
||||
}
|
||||
else {
|
||||
assert(!other.is_unaligned);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class InnerNode : public BVHNode {
|
||||
public:
|
||||
static constexpr int kNumMaxChildren = 8;
|
||||
|
||||
InnerNode(const BoundBox &bounds, unique_ptr<BVHNode> &&child0, unique_ptr<BVHNode> &&child1)
|
||||
: BVHNode(bounds), num_children_(2)
|
||||
{
|
||||
if (child0 && child1) {
|
||||
visibility = child0->visibility | child1->visibility;
|
||||
}
|
||||
else {
|
||||
/* Happens on build cancel. */
|
||||
visibility = 0;
|
||||
}
|
||||
|
||||
children[0] = std::move(child0);
|
||||
children[1] = std::move(child1);
|
||||
}
|
||||
|
||||
/* NOTE: This function is only used during binary BVH builder, and it's
|
||||
* supposed to be configured to have 2 children which will be filled-in in a
|
||||
* bit. */
|
||||
explicit InnerNode(const BoundBox &bounds) : BVHNode(bounds), num_children_(0)
|
||||
{
|
||||
visibility = 0;
|
||||
num_children_ = 2;
|
||||
}
|
||||
|
||||
bool is_leaf() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int num_children() const override
|
||||
{
|
||||
return num_children_;
|
||||
}
|
||||
BVHNode *get_child(const int i) const override
|
||||
{
|
||||
assert(i >= 0 && i < num_children_);
|
||||
return children[i].get();
|
||||
}
|
||||
void print(const int depth) const override;
|
||||
|
||||
int num_children_;
|
||||
unique_ptr<BVHNode> children[kNumMaxChildren];
|
||||
};
|
||||
|
||||
class LeafNode : public BVHNode {
|
||||
public:
|
||||
LeafNode(const BoundBox &bounds, const uint visibility, const int lo, const int hi)
|
||||
: BVHNode(bounds), lo(lo), hi(hi)
|
||||
{
|
||||
this->bounds = bounds;
|
||||
this->visibility = visibility;
|
||||
}
|
||||
|
||||
LeafNode(const LeafNode &other) = default;
|
||||
|
||||
bool is_leaf() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int num_children() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
BVHNode *get_child(int /*i*/) const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
int num_triangles() const override
|
||||
{
|
||||
return hi - lo;
|
||||
}
|
||||
void print(const int depth) const override;
|
||||
|
||||
int lo;
|
||||
int hi;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
473
blender-5.2.0/intern/cycles/bvh/octree.cpp
Normal file
473
blender-5.2.0/intern/cycles/bvh/octree.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "bvh/octree.h"
|
||||
|
||||
#include "scene/object.h"
|
||||
#include "scene/volume.h"
|
||||
|
||||
#include "integrator/shader_eval.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/progress.h"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/tools/FindActiveValues.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
__forceinline int Octree::flatten_index(int x, int y, int z) const
|
||||
{
|
||||
return x + resolution_ * (y + z * resolution_);
|
||||
}
|
||||
|
||||
Extrema<float> Octree::get_extrema(const int3 index_min, const int3 index_max) const
|
||||
{
|
||||
const blocked_range3d<int> range(
|
||||
index_min.x, index_max.x, 32, index_min.y, index_max.y, 32, index_min.z, index_max.z, 32);
|
||||
|
||||
const Extrema<float> identity = {FLT_MAX, -FLT_MAX};
|
||||
|
||||
auto reduction_func = [&](const blocked_range3d<int> &r, Extrema<float> init) -> Extrema<float> {
|
||||
for (int z = r.cols().begin(); z < r.cols().end(); ++z) {
|
||||
for (int y = r.rows().begin(); y < r.rows().end(); ++y) {
|
||||
for (int x = r.pages().begin(); x < r.pages().end(); ++x) {
|
||||
init = merge(init, sigmas_[flatten_index(x, y, z)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return init;
|
||||
};
|
||||
|
||||
auto join_func = [](Extrema<float> a, Extrema<float> b) -> Extrema<float> {
|
||||
return merge(a, b);
|
||||
};
|
||||
|
||||
return parallel_reduce(range, identity, reduction_func, join_func);
|
||||
}
|
||||
|
||||
__forceinline float3 Octree::position_to_index(const float3 p) const
|
||||
{
|
||||
return (p - bbox_min) * position_to_index_scale_;
|
||||
}
|
||||
|
||||
int3 Octree::position_to_floor_index(const float3 p) const
|
||||
{
|
||||
const float3 index = round(position_to_index(p));
|
||||
return clamp(make_int3(int(index.x), int(index.y), int(index.z)), 0, resolution_ - 1);
|
||||
}
|
||||
|
||||
int3 Octree::position_to_ceil_index(const float3 p) const
|
||||
{
|
||||
if (any_zero(position_to_index_scale_)) {
|
||||
/* Octree with degenerate shape, force max index. */
|
||||
return make_int3(resolution_);
|
||||
}
|
||||
const float3 index = round(position_to_index(p));
|
||||
return clamp(make_int3(int(index.x), int(index.y), int(index.z)), 1, resolution_);
|
||||
}
|
||||
|
||||
__forceinline float3 Octree::index_to_position(int x, int y, int z) const
|
||||
{
|
||||
return bbox_min + make_float3(x, y, z) * index_to_position_scale_;
|
||||
}
|
||||
|
||||
__forceinline float3 Octree::voxel_size() const
|
||||
{
|
||||
return index_to_position_scale_;
|
||||
}
|
||||
|
||||
bool Octree::should_split(std::shared_ptr<OctreeNode> &node) const
|
||||
{
|
||||
const int3 index_min = position_to_floor_index(node->bbox.min);
|
||||
const int3 index_max = position_to_ceil_index(node->bbox.max);
|
||||
node->sigma = get_extrema(index_min, index_max);
|
||||
|
||||
const float3 bbox_size = node->bbox.size();
|
||||
if (any_zero(bbox_size)) {
|
||||
/* Octree with degenerate shape, can happen for implicit volume. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The threshold is set so that ideally only one sample needs to be taken per node. Value taken
|
||||
* from "Volume Rendering for Pixar's Elemental". */
|
||||
return (node->sigma.range() * len(bbox_size) * scale_ > 1.442f &&
|
||||
node->depth < VOLUME_OCTREE_MAX_DEPTH);
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Check if a interior mask grid intersects with a bounding box defined by `p_min` and `p_max`. */
|
||||
static bool vdb_voxel_intersect(const float3 p_min,
|
||||
const float3 p_max,
|
||||
openvdb::BoolGrid::ConstPtr &grid,
|
||||
const openvdb::tools::FindActiveValues<openvdb::BoolTree> &find)
|
||||
{
|
||||
if (grid->empty()) {
|
||||
/* Non-mesh volume or open mesh. */
|
||||
return true;
|
||||
}
|
||||
|
||||
const openvdb::math::CoordBBox coord_bbox(
|
||||
openvdb::Coord::floor(grid->worldToIndex({p_min.x, p_min.y, p_min.z})),
|
||||
openvdb::Coord::ceil(grid->worldToIndex({p_max.x, p_max.y, p_max.z})));
|
||||
|
||||
/* Check if the bounding box lies inside or partially overlaps the mesh.
|
||||
* For interior mask grids, all the interior voxels are active. */
|
||||
return find.anyActiveValues(coord_bbox, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fill in coordinates for shading the volume density. */
|
||||
static void fill_shader_input(device_vector<KernelShaderEvalInput> &d_input,
|
||||
const Octree *octree,
|
||||
const Object *object,
|
||||
const Shader *shader,
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::BoolGrid::ConstPtr &interior_mask,
|
||||
#endif
|
||||
const int resolution)
|
||||
{
|
||||
const int object_id = object->get_device_index();
|
||||
const uint shader_id = shader->id;
|
||||
|
||||
KernelShaderEvalInput *d_input_data = d_input.data();
|
||||
|
||||
const float3 voxel_size = octree->voxel_size();
|
||||
/* Dilate the voxel in case we miss features at the boundary. */
|
||||
const float3 pad = 0.2f * voxel_size;
|
||||
const float3 padded_size = voxel_size + pad * 2.0f;
|
||||
|
||||
const blocked_range3d<int> range(0, resolution, 8, 0, resolution, 8, 0, resolution, 8);
|
||||
parallel_for(range, [&](const blocked_range3d<int> &r) {
|
||||
#ifdef WITH_OPENVDB
|
||||
/* One accessor per thread is important for cached access. */
|
||||
const auto find = openvdb::tools::FindActiveValues(interior_mask->tree());
|
||||
#endif
|
||||
|
||||
for (int z = r.cols().begin(); z < r.cols().end(); ++z) {
|
||||
for (int y = r.rows().begin(); y < r.rows().end(); ++y) {
|
||||
for (int x = r.pages().begin(); x < r.pages().end(); ++x) {
|
||||
const int offset = octree->flatten_index(x, y, z);
|
||||
const float3 p = octree->index_to_position(x, y, z);
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
/* Zero density for cells outside of the mesh. */
|
||||
if (!vdb_voxel_intersect(p, p + voxel_size, interior_mask, find)) {
|
||||
d_input_data[offset * 2].object = OBJECT_NONE;
|
||||
d_input_data[offset * 2 + 1].object = SHADER_NONE;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
KernelShaderEvalInput in;
|
||||
in.object = object_id;
|
||||
in.prim = __float_as_int(p.x - pad.x);
|
||||
in.u = p.y - pad.y;
|
||||
in.v = p.z - pad.z;
|
||||
d_input_data[offset * 2] = in;
|
||||
|
||||
in.object = shader_id;
|
||||
in.prim = __float_as_int(padded_size.x);
|
||||
in.u = padded_size.y;
|
||||
in.v = padded_size.z;
|
||||
d_input_data[offset * 2 + 1] = in;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Read back the volume density. */
|
||||
static void read_shader_output(const device_vector<float> &d_output,
|
||||
const Octree *octree,
|
||||
const int num_channels,
|
||||
const int resolution,
|
||||
vector<Extrema<float>> &sigmas)
|
||||
{
|
||||
const float *d_output_data = d_output.data();
|
||||
const blocked_range3d<int> range(0, resolution, 32, 0, resolution, 32, 0, resolution, 32);
|
||||
|
||||
parallel_for(range, [&](const blocked_range3d<int> &r) {
|
||||
for (int z = r.cols().begin(); z < r.cols().end(); ++z) {
|
||||
for (int y = r.rows().begin(); y < r.rows().end(); ++y) {
|
||||
for (int x = r.pages().begin(); x < r.pages().end(); ++x) {
|
||||
const int index = octree->flatten_index(x, y, z);
|
||||
sigmas[index].min = d_output_data[index * num_channels + 0];
|
||||
sigmas[index].max = d_output_data[index * num_channels + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Octree::evaluate_volume_density(Device *device,
|
||||
Progress &progress,
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::BoolGrid::ConstPtr &interior_mask,
|
||||
#endif
|
||||
const Object *object,
|
||||
const Shader *shader)
|
||||
{
|
||||
/* For heterogeneous volume, the grid resolution is 2^max_depth in each 3D dimension;
|
||||
* for homogeneous volume, only one grid is needed. */
|
||||
resolution_ = VolumeManager::is_homogeneous_volume(object, shader) ?
|
||||
1 :
|
||||
power_of_2(VOLUME_OCTREE_MAX_DEPTH);
|
||||
index_to_position_scale_ = root_->bbox.size() / float(resolution_);
|
||||
position_to_index_scale_ = safe_divide(one_float3(), index_to_position_scale_);
|
||||
|
||||
/* Initialize density field. */
|
||||
/* TODO(weizhen): maybe lower the resolution depending on the object size. */
|
||||
const int size = resolution_ * resolution_ * resolution_;
|
||||
sigmas_.resize(size);
|
||||
parallel_for(0, size, [&](int i) { sigmas_[i] = {0.0f, 0.0f}; });
|
||||
|
||||
/* Min and max. */
|
||||
const int num_channels = 2;
|
||||
|
||||
/* Need the size of two `KernelShaderEvalInput`s per voxel for evaluating the shader. */
|
||||
const int num_inputs = size * 2;
|
||||
|
||||
/* Evaluate shader on device. */
|
||||
ShaderEval shader_eval(device, progress);
|
||||
shader_eval.eval(
|
||||
SHADER_EVAL_VOLUME_DENSITY,
|
||||
num_inputs,
|
||||
num_channels,
|
||||
[&](device_vector<KernelShaderEvalInput> &d_input) {
|
||||
#ifdef WITH_OPENVDB
|
||||
fill_shader_input(d_input, this, object, shader, interior_mask, resolution_);
|
||||
#else
|
||||
fill_shader_input(d_input, this, object, shader, resolution_);
|
||||
#endif
|
||||
return size;
|
||||
},
|
||||
[&](device_vector<float> &d_output) {
|
||||
read_shader_output(d_output, this, num_channels, resolution_, sigmas_);
|
||||
});
|
||||
}
|
||||
|
||||
float Octree::volume_scale(const Object *object) const
|
||||
{
|
||||
const Geometry *geom = object->get_geometry();
|
||||
if (geom->is_volume()) {
|
||||
const Volume *volume = static_cast<const Volume *>(geom);
|
||||
if (volume->get_object_space()) {
|
||||
/* The density changes with object scale, we scale the density accordingly in the final
|
||||
* render. */
|
||||
if (volume->transform_applied) {
|
||||
const float3 unit = normalize(one_float3());
|
||||
return 1.0f / len(transform_direction(&object->get_tfm(), unit));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* The density does not change with object scale, we scale the node in the viewport to it's
|
||||
* true size. */
|
||||
if (!volume->transform_applied) {
|
||||
const float3 unit = normalize(one_float3());
|
||||
return len(transform_direction(&object->get_tfm(), unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO(weizhen): use the maximal scale of all instances. */
|
||||
if (!geom->transform_applied) {
|
||||
const float3 unit = normalize(one_float3());
|
||||
return len(transform_direction(&object->get_tfm(), unit));
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
std::shared_ptr<OctreeInternalNode> Octree::make_internal(std::shared_ptr<OctreeNode> &node)
|
||||
{
|
||||
num_nodes_ += 8;
|
||||
auto internal = std::make_shared<OctreeInternalNode>(*node);
|
||||
|
||||
/* Create bounding boxes for children. */
|
||||
const float3 center = internal->bbox.center();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const float3 t = make_float3(i & 1, (i >> 1) & 1, (i >> 2) & 1);
|
||||
const BoundBox bbox(mix(internal->bbox.min, center, t), mix(center, internal->bbox.max, t));
|
||||
internal->children_[i] = std::make_shared<OctreeNode>(bbox, internal->depth + 1);
|
||||
}
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
void Octree::recursive_build(std::shared_ptr<OctreeNode> &octree_node)
|
||||
{
|
||||
if (!should_split(octree_node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make the current node an internal node. */
|
||||
auto internal = make_internal(octree_node);
|
||||
|
||||
for (auto &child : internal->children_) {
|
||||
task_pool_.push([&] { recursive_build(child); });
|
||||
}
|
||||
|
||||
octree_node = internal;
|
||||
}
|
||||
|
||||
void Octree::flatten(KernelOctreeNode *knodes,
|
||||
const int current_index,
|
||||
const std::shared_ptr<OctreeNode> &node,
|
||||
int &child_index) const
|
||||
{
|
||||
KernelOctreeNode &knode = knodes[current_index];
|
||||
knode.sigma = node->sigma;
|
||||
|
||||
if (auto internal_ptr = std::dynamic_pointer_cast<OctreeInternalNode>(node)) {
|
||||
knode.first_child = child_index;
|
||||
child_index += 8;
|
||||
/* Loop through all the children and flatten in breadth-first manner, so that children are
|
||||
* stored in contiguous indices. */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
knodes[knode.first_child + i].parent = current_index;
|
||||
flatten(knodes, knode.first_child + i, internal_ptr->children_[i], child_index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
knode.first_child = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::set_flattened(const bool flattened)
|
||||
{
|
||||
is_flattened_ = flattened;
|
||||
}
|
||||
|
||||
bool Octree::is_flattened() const
|
||||
{
|
||||
return is_flattened_;
|
||||
}
|
||||
|
||||
void Octree::build(Device *device,
|
||||
Progress &progress,
|
||||
#ifdef WITH_OPENVDB
|
||||
openvdb::BoolGrid::ConstPtr &interior_mask,
|
||||
#endif
|
||||
const Object *object,
|
||||
const Shader *shader)
|
||||
{
|
||||
const char *name = object->get_asset_name().c_str();
|
||||
progress.set_substatus(string_printf("Evaluating density for %s", name));
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
evaluate_volume_density(device, progress, interior_mask, object, shader);
|
||||
#else
|
||||
evaluate_volume_density(device, progress, object, shader);
|
||||
#endif
|
||||
if (progress.get_cancel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
progress.set_substatus(string_printf("Building octree for %s", name));
|
||||
|
||||
scale_ = volume_scale(object);
|
||||
recursive_build(root_);
|
||||
|
||||
task_pool_.wait_work();
|
||||
|
||||
is_built_ = true;
|
||||
sigmas_.clear();
|
||||
}
|
||||
|
||||
Octree::Octree(const BoundBox &bbox)
|
||||
{
|
||||
bbox_min = bbox.min;
|
||||
root_ = std::make_shared<OctreeNode>(bbox, 0);
|
||||
is_built_ = false;
|
||||
is_flattened_ = false;
|
||||
}
|
||||
|
||||
bool Octree::is_built() const
|
||||
{
|
||||
return is_built_;
|
||||
}
|
||||
|
||||
int Octree::get_num_nodes() const
|
||||
{
|
||||
return num_nodes_;
|
||||
}
|
||||
|
||||
std::shared_ptr<OctreeNode> Octree::get_root() const
|
||||
{
|
||||
return root_;
|
||||
}
|
||||
|
||||
void OctreeNode::visualize(std::string &str) const
|
||||
{
|
||||
const auto *internal = dynamic_cast<const OctreeInternalNode *>(this);
|
||||
|
||||
if (!internal) {
|
||||
/* Skip leaf nodes. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create three orthogonal faces for inner nodes. */
|
||||
const float3 mid = bbox.center();
|
||||
const float3 max = bbox.max;
|
||||
const float3 min = bbox.min;
|
||||
const std::string mid_x = to_string(mid.x), mid_y = to_string(mid.y), mid_z = to_string(mid.z),
|
||||
min_x = to_string(min.x), min_y = to_string(min.y), min_z = to_string(min.z),
|
||||
max_x = to_string(max.x), max_y = to_string(max.y), max_z = to_string(max.z);
|
||||
// clang-format off
|
||||
str += "(" + mid_x + "," + mid_y + "," + min_z + "), "
|
||||
"(" + mid_x + "," + mid_y + "," + max_z + "), "
|
||||
"(" + mid_x + "," + max_y + "," + max_z + "), "
|
||||
"(" + mid_x + "," + max_y + "," + min_z + "), "
|
||||
"(" + mid_x + "," + min_y + "," + min_z + "), "
|
||||
"(" + mid_x + "," + min_y + "," + max_z + "), ";
|
||||
str += "(" + min_x + "," + mid_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + mid_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + mid_y + "," + max_z + "), "
|
||||
"(" + min_x + "," + mid_y + "," + max_z + "), "
|
||||
"(" + min_x + "," + mid_y + "," + min_z + "), "
|
||||
"(" + max_x + "," + mid_y + "," + min_z + "), ";
|
||||
str += "(" + mid_x + "," + min_y + "," + mid_z + "), "
|
||||
"(" + mid_x + "," + max_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + max_y + "," + mid_z + "), "
|
||||
"(" + max_x + "," + min_y + "," + mid_z + "), "
|
||||
"(" + min_x + "," + min_y + "," + mid_z + "), "
|
||||
"(" + min_x + "," + max_y + "," + mid_z + "), ";
|
||||
// clang-format on
|
||||
for (const auto &child : internal->children_) {
|
||||
child->visualize(str);
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::visualize(std::ofstream &file, const std::string object_name) const
|
||||
{
|
||||
std::string str = "vertices = [";
|
||||
root_->visualize(str);
|
||||
str +=
|
||||
"]\nr = range(len(vertices))\n"
|
||||
"edges = [(i, i+1 if i%6<5 else i-4) for i in r]\n"
|
||||
"mesh = bpy.data.meshes.new('Octree')\n"
|
||||
"mesh.from_pydata(vertices, edges, [])\n"
|
||||
"mesh.update()\n"
|
||||
"obj = bpy.data.objects.new('" +
|
||||
object_name +
|
||||
"', mesh)\n"
|
||||
"octree.objects.link(obj)\n"
|
||||
"bpy.context.view_layer.objects.active = obj\n"
|
||||
"bpy.ops.object.mode_set(mode='EDIT')\n";
|
||||
file << str;
|
||||
|
||||
const float3 center = root_->bbox.center();
|
||||
const float3 size = root_->bbox.size() * 0.5f;
|
||||
file << "bpy.ops.mesh.primitive_cube_add(location = " << center << ", scale = " << size << ")\n";
|
||||
file << "bpy.ops.mesh.delete(type='ONLY_FACE')\n"
|
||||
"bpy.ops.object.mode_set(mode='OBJECT')\n"
|
||||
"obj.select_set(True)\n";
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
145
blender-5.2.0/intern/cycles/bvh/octree.h
Normal file
145
blender-5.2.0/intern/cycles/bvh/octree.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* The volume octree is used to determine the necessary step size when rendering the volume. One
|
||||
* volume per object per shader is built, and a node splits in eight when the density difference
|
||||
* inside the node exceeds a certain threshold. */
|
||||
|
||||
#ifndef __OCTREE_H__
|
||||
#define __OCTREE_H__
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/task.h"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/openvdb.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <iosfwd>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Device;
|
||||
class Object;
|
||||
class Progress;
|
||||
class Shader;
|
||||
struct KernelOctreeNode;
|
||||
|
||||
struct OctreeNode {
|
||||
/* Bounding box of the node. */
|
||||
BoundBox bbox;
|
||||
|
||||
/* Depth of the node in the octree. */
|
||||
int depth;
|
||||
|
||||
/* Minimal and maximal volume density inside the node. */
|
||||
Extrema<float> sigma = {0.0f, 0.0f};
|
||||
|
||||
OctreeNode() : bbox(BoundBox::empty), depth(0) {}
|
||||
OctreeNode(BoundBox bbox_, int depth_) : bbox(bbox_), depth(depth_) {}
|
||||
virtual ~OctreeNode() = default;
|
||||
|
||||
/* Visualize node. */
|
||||
void visualize(std::string &str) const;
|
||||
};
|
||||
|
||||
struct OctreeInternalNode : public OctreeNode {
|
||||
OctreeInternalNode(OctreeNode &node) : children_(8)
|
||||
{
|
||||
bbox = node.bbox;
|
||||
depth = node.depth;
|
||||
sigma = node.sigma;
|
||||
}
|
||||
|
||||
vector<std::shared_ptr<OctreeNode>> children_;
|
||||
};
|
||||
|
||||
class Octree {
|
||||
public:
|
||||
Octree(const BoundBox &bbox);
|
||||
~Octree() = default;
|
||||
|
||||
/* Build the octree according to the volume density. */
|
||||
#ifdef WITH_OPENVDB
|
||||
void build(Device *, Progress &, openvdb::BoolGrid::ConstPtr &, const Object *, const Shader *);
|
||||
#else
|
||||
void build(Device *, Progress &, const Object *, const Shader *);
|
||||
#endif
|
||||
|
||||
/* Convert the octree into an array of nodes for uploading to the kernel. */
|
||||
void flatten(KernelOctreeNode *, const int, const std::shared_ptr<OctreeNode> &, int &) const;
|
||||
void set_flattened(const bool = true);
|
||||
bool is_flattened() const;
|
||||
|
||||
/* Flatten a 3D coordinate in the grid to a 1D index. */
|
||||
int flatten_index(int x, int y, int z) const;
|
||||
/* Convert from index to the position of the lower left corner of the voxel. */
|
||||
float3 index_to_position(int x, int y, int z) const;
|
||||
/* Size of a voxel. */
|
||||
float3 voxel_size() const;
|
||||
|
||||
int get_num_nodes() const;
|
||||
std::shared_ptr<OctreeNode> get_root() const;
|
||||
bool is_built() const;
|
||||
|
||||
/* Draw octree nodes as empty boxes with Blender Python API. */
|
||||
void visualize(std::ofstream &file, const std::string object_name) const;
|
||||
|
||||
private:
|
||||
/* The bounding box of the octree is divided into a regular grid with the same resolution in each
|
||||
* dimension. */
|
||||
int resolution_;
|
||||
/* Extrema of volume densities in the grid. */
|
||||
vector<Extrema<float>> sigmas_;
|
||||
/* Compute the extrema of all the `sigmas_` in a coordinate bounding box defined by `index_min`
|
||||
* and `index_max`. */
|
||||
Extrema<float> get_extrema(const int3 index_min, const int3 index_max) const;
|
||||
/* Randomly sample positions inside the grid to evaluate the shader for the density. */
|
||||
#ifdef WITH_OPENVDB
|
||||
void evaluate_volume_density(
|
||||
Device *, Progress &, openvdb::BoolGrid::ConstPtr &, const Object *, const Shader *);
|
||||
#else
|
||||
void evaluate_volume_density(Device *, Progress &, const Object *, const Shader *);
|
||||
#endif
|
||||
/* Convert from position in object space to grid index space. */
|
||||
float3 position_to_index_scale_;
|
||||
float3 index_to_position_scale_;
|
||||
float3 position_to_index(const float3 p) const;
|
||||
int3 position_to_floor_index(const float3 p) const;
|
||||
int3 position_to_ceil_index(const float3 p) const;
|
||||
|
||||
/* Whether a node should be split into child nodes. */
|
||||
bool should_split(std::shared_ptr<OctreeNode> &node) const;
|
||||
/* Scale the node size so that the octree has the similar subdivision levels in viewport and
|
||||
* final render. */
|
||||
float volume_scale(const Object *object) const;
|
||||
float scale_;
|
||||
/* Recursively build a node and its child nodes. */
|
||||
void recursive_build(std::shared_ptr<OctreeNode> &);
|
||||
/* Turn a node into an internal node. */
|
||||
std::shared_ptr<OctreeInternalNode> make_internal(std::shared_ptr<OctreeNode> &node);
|
||||
|
||||
/* Root node. */
|
||||
std::shared_ptr<OctreeNode> root_;
|
||||
|
||||
/* Bounding box min of the octree, used for computing the indices. */
|
||||
float3 bbox_min;
|
||||
|
||||
/* Whether the octree is already built. */
|
||||
bool is_built_;
|
||||
|
||||
/* Whether the octree is already flattened into an array. */
|
||||
bool is_flattened_;
|
||||
|
||||
/* Number of nodes in the octree. Incremented while building the tree. */
|
||||
std::atomic<int> num_nodes_ = 1;
|
||||
|
||||
/* Task pool for building the octree in parallel. */
|
||||
TaskPool task_pool_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __OCTREE_H__ */
|
||||
37
blender-5.2.0/intern/cycles/bvh/optix.cpp
Normal file
37
blender-5.2.0/intern/cycles/bvh/optix.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/* SPDX-FileCopyrightText: 2019 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_OPTIX
|
||||
|
||||
# include "device/device.h"
|
||||
|
||||
# include "bvh/optix.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHOptiX::BVHOptiX(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_,
|
||||
Device *device)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
device(device),
|
||||
traversable_handle(0),
|
||||
as_data(make_unique<device_only_memory<char>>(
|
||||
device, params.top_level ? "optix tlas" : "optix blas", false)),
|
||||
motion_transform_data(
|
||||
make_unique<device_only_memory<char>>(device, "optix motion transform", false))
|
||||
{
|
||||
}
|
||||
|
||||
BVHOptiX::~BVHOptiX()
|
||||
{
|
||||
/* Acceleration structure memory is delayed freed on device, since deleting the
|
||||
* BVH may happen while still being used for rendering. */
|
||||
device->release_bvh(this);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_OPTIX */
|
||||
35
blender-5.2.0/intern/cycles/bvh/optix.h
Normal file
35
blender-5.2.0/intern/cycles/bvh/optix.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2019 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_OPTIX
|
||||
|
||||
# include "bvh/bvh.h"
|
||||
# include "bvh/params.h"
|
||||
|
||||
# include "device/memory.h"
|
||||
|
||||
# include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHOptiX : public BVH {
|
||||
public:
|
||||
Device *device;
|
||||
uint64_t traversable_handle;
|
||||
unique_ptr<device_only_memory<char>> as_data;
|
||||
unique_ptr<device_only_memory<char>> motion_transform_data;
|
||||
|
||||
BVHOptiX(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
~BVHOptiX() override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_OPTIX */
|
||||
325
blender-5.2.0/intern/cycles/bvh/params.h
Normal file
325
blender-5.2.0/intern/cycles/bvh/params.h
Normal file
@@ -0,0 +1,325 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Layout of BVH tree.
|
||||
*
|
||||
* For example, how wide BVH tree is, in terms of number of children
|
||||
* per node.
|
||||
*/
|
||||
using BVHLayout = KernelBVHLayout;
|
||||
|
||||
/* Type of BVH, in terms whether it is supported dynamic updates of meshes
|
||||
* or whether modifying geometry requires full BVH rebuild.
|
||||
*/
|
||||
enum BVHType {
|
||||
/* BVH supports dynamic updates of geometry.
|
||||
*
|
||||
* Faster for updating BVH tree when doing modifications in viewport,
|
||||
* but slower for rendering.
|
||||
*/
|
||||
BVH_TYPE_DYNAMIC = 0,
|
||||
/* BVH tree is calculated for specific scene, updates in geometry
|
||||
* requires full tree rebuild.
|
||||
*
|
||||
* Slower to update BVH tree when modifying objects in viewport, also
|
||||
* slower to build final BVH tree but gives best possible render speed.
|
||||
*/
|
||||
BVH_TYPE_STATIC = 1,
|
||||
|
||||
BVH_NUM_TYPES,
|
||||
};
|
||||
|
||||
/* Names bit-flag type to denote which BVH layouts are supported by
|
||||
* particular area.
|
||||
*
|
||||
* Bit-flags are the BVH_LAYOUT_* values.
|
||||
*/
|
||||
using BVHLayoutMask = int;
|
||||
|
||||
/* Get human readable name of BVH layout. */
|
||||
const char *bvh_layout_name(BVHLayout layout);
|
||||
|
||||
/* BVH Parameters */
|
||||
|
||||
class BVHParams {
|
||||
public:
|
||||
/* spatial split area threshold */
|
||||
bool use_spatial_split;
|
||||
float spatial_split_alpha;
|
||||
|
||||
/* Unaligned nodes creation threshold */
|
||||
float unaligned_split_threshold;
|
||||
|
||||
/* SAH costs */
|
||||
float sah_node_cost;
|
||||
float sah_primitive_cost;
|
||||
|
||||
/* number of primitives in leaf */
|
||||
int min_leaf_size;
|
||||
int max_triangle_leaf_size;
|
||||
int max_motion_triangle_leaf_size;
|
||||
int max_curve_leaf_size;
|
||||
int max_motion_curve_leaf_size;
|
||||
int max_point_leaf_size;
|
||||
int max_motion_point_leaf_size;
|
||||
|
||||
/* object or mesh level bvh */
|
||||
bool top_level;
|
||||
|
||||
/* BVH layout to be built. */
|
||||
BVHLayout bvh_layout;
|
||||
|
||||
/* Use unaligned bounding boxes.
|
||||
* Only used for curves BVH.
|
||||
*/
|
||||
bool use_unaligned_nodes;
|
||||
|
||||
/* Use compact acceleration structure (Embree). */
|
||||
bool use_compact_structure;
|
||||
|
||||
/* Split time range to this number of steps and create leaf node for each
|
||||
* of this time steps.
|
||||
*
|
||||
* Speeds up rendering of motion primitives at the cost of higher memory usage.
|
||||
*/
|
||||
|
||||
/* Same as above, but for triangle primitives. */
|
||||
int num_motion_triangle_steps;
|
||||
int num_motion_curve_steps;
|
||||
int num_motion_point_steps;
|
||||
|
||||
/* Same as in SceneParams. */
|
||||
int bvh_type;
|
||||
|
||||
/* These are needed for Embree. */
|
||||
int curve_subdivisions;
|
||||
|
||||
/* fixed parameters */
|
||||
enum { MAX_DEPTH = 64, MAX_SPATIAL_DEPTH = 48, NUM_SPATIAL_BINS = 32 };
|
||||
|
||||
BVHParams()
|
||||
{
|
||||
use_spatial_split = true;
|
||||
spatial_split_alpha = 1e-5f;
|
||||
|
||||
unaligned_split_threshold = 0.7f;
|
||||
|
||||
/* todo: see if splitting up primitive cost to be separate for triangles
|
||||
* and curves can help. so far in tests it doesn't help, but why? */
|
||||
sah_node_cost = 1.0f;
|
||||
sah_primitive_cost = 1.0f;
|
||||
|
||||
min_leaf_size = 1;
|
||||
max_triangle_leaf_size = 8;
|
||||
max_motion_triangle_leaf_size = 8;
|
||||
max_curve_leaf_size = 1;
|
||||
max_motion_curve_leaf_size = 4;
|
||||
max_point_leaf_size = 8;
|
||||
max_motion_point_leaf_size = 8;
|
||||
|
||||
top_level = false;
|
||||
bvh_layout = BVH_LAYOUT_BVH2;
|
||||
use_compact_structure = false;
|
||||
use_unaligned_nodes = false;
|
||||
|
||||
num_motion_curve_steps = 0;
|
||||
num_motion_triangle_steps = 0;
|
||||
num_motion_point_steps = 0;
|
||||
|
||||
bvh_type = 0;
|
||||
|
||||
curve_subdivisions = 4;
|
||||
}
|
||||
|
||||
/* SAH costs */
|
||||
__forceinline float cost(const int num_nodes, const int num_primitives) const
|
||||
{
|
||||
return node_cost(num_nodes) + primitive_cost(num_primitives);
|
||||
}
|
||||
|
||||
__forceinline float primitive_cost(const int n) const
|
||||
{
|
||||
return n * sah_primitive_cost;
|
||||
}
|
||||
|
||||
__forceinline float node_cost(const int n) const
|
||||
{
|
||||
return n * sah_node_cost;
|
||||
}
|
||||
|
||||
__forceinline bool small_enough_for_leaf(const int size, const int level)
|
||||
{
|
||||
return (size <= min_leaf_size || level >= MAX_DEPTH);
|
||||
}
|
||||
|
||||
bool use_motion_steps()
|
||||
{
|
||||
return num_motion_curve_steps > 0 || num_motion_triangle_steps > 0 ||
|
||||
num_motion_point_steps > 0;
|
||||
}
|
||||
|
||||
/* Gets best matching BVH.
|
||||
*
|
||||
* If the requested layout is supported by the device, it will be used.
|
||||
* Otherwise, widest supported layout below that will be used.
|
||||
*/
|
||||
static BVHLayout best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask supported_layouts);
|
||||
};
|
||||
|
||||
/* BVH Reference
|
||||
*
|
||||
* Reference to a primitive. Primitive index and object are sneakily packed
|
||||
* into BoundBox to reduce memory usage and align nicely */
|
||||
|
||||
class BVHReference {
|
||||
public:
|
||||
__forceinline BVHReference() = default;
|
||||
|
||||
__forceinline BVHReference(const BoundBox &bounds_,
|
||||
const int prim_index_,
|
||||
const int prim_object_,
|
||||
const int prim_type,
|
||||
float time_from = 0.0f,
|
||||
float time_to = 1.0f)
|
||||
: rbounds(bounds_), time_from_(time_from), time_to_(time_to)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(prim_index_);
|
||||
rbounds.max.w = __int_as_float(prim_object_);
|
||||
type = prim_type;
|
||||
}
|
||||
|
||||
__forceinline const BoundBox &bounds() const
|
||||
{
|
||||
return rbounds;
|
||||
}
|
||||
__forceinline int prim_index() const
|
||||
{
|
||||
return __float_as_int(rbounds.min.w);
|
||||
}
|
||||
__forceinline int prim_object() const
|
||||
{
|
||||
return __float_as_int(rbounds.max.w);
|
||||
}
|
||||
__forceinline int prim_type() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
__forceinline float time_from() const
|
||||
{
|
||||
return time_from_;
|
||||
}
|
||||
__forceinline float time_to() const
|
||||
{
|
||||
return time_to_;
|
||||
}
|
||||
|
||||
BVHReference &operator=(const BVHReference &arg) = default;
|
||||
|
||||
protected:
|
||||
BoundBox rbounds;
|
||||
uint type;
|
||||
float time_from_, time_to_;
|
||||
};
|
||||
|
||||
/* BVH Range
|
||||
*
|
||||
* Build range used during construction, to indicate the bounds and place in
|
||||
* the reference array of a subset of primitives Again uses trickery to pack
|
||||
* integers into BoundBox for alignment purposes. */
|
||||
|
||||
class BVHRange {
|
||||
public:
|
||||
__forceinline BVHRange()
|
||||
{
|
||||
rbounds.min.w = __int_as_float(0);
|
||||
rbounds.max.w = __int_as_float(0);
|
||||
}
|
||||
|
||||
__forceinline BVHRange(const BoundBox &bounds_, int start_, int size_) : rbounds(bounds_)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(start_);
|
||||
rbounds.max.w = __int_as_float(size_);
|
||||
}
|
||||
|
||||
__forceinline BVHRange(const BoundBox &bounds_, const BoundBox &cbounds_, int start_, int size_)
|
||||
: rbounds(bounds_), cbounds(cbounds_)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(start_);
|
||||
rbounds.max.w = __int_as_float(size_);
|
||||
}
|
||||
|
||||
__forceinline void set_start(const int start_)
|
||||
{
|
||||
rbounds.min.w = __int_as_float(start_);
|
||||
}
|
||||
|
||||
__forceinline const BoundBox &bounds() const
|
||||
{
|
||||
return rbounds;
|
||||
}
|
||||
__forceinline const BoundBox ¢_bounds() const
|
||||
{
|
||||
return cbounds;
|
||||
}
|
||||
__forceinline int start() const
|
||||
{
|
||||
return __float_as_int(rbounds.min.w);
|
||||
}
|
||||
__forceinline int size() const
|
||||
{
|
||||
return __float_as_int(rbounds.max.w);
|
||||
}
|
||||
__forceinline int end() const
|
||||
{
|
||||
return start() + size();
|
||||
}
|
||||
|
||||
protected:
|
||||
BoundBox rbounds;
|
||||
BoundBox cbounds;
|
||||
};
|
||||
|
||||
/* BVH Spatial Bin */
|
||||
|
||||
struct BVHSpatialBin {
|
||||
BoundBox bounds;
|
||||
int enter;
|
||||
int exit;
|
||||
|
||||
__forceinline BVHSpatialBin() = default;
|
||||
};
|
||||
|
||||
/* BVH Spatial Storage
|
||||
*
|
||||
* The idea of this storage is have thread-specific storage for the spatial
|
||||
* splitters. We can pre-allocate this storage in advance and avoid heavy memory
|
||||
* operations during split process.
|
||||
*/
|
||||
|
||||
struct BVHSpatialStorage {
|
||||
/* Accumulated bounds when sweeping from right to left. */
|
||||
vector<BoundBox> right_bounds;
|
||||
|
||||
/* Bins used for histogram when selecting best split plane. */
|
||||
BVHSpatialBin bins[3][BVHParams::NUM_SPATIAL_BINS];
|
||||
|
||||
/* Temporary storage for the new references. Used by spatial split to store
|
||||
* new references in before they're getting inserted into actual array,
|
||||
*/
|
||||
vector<BVHReference> new_references;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
190
blender-5.2.0/intern/cycles/bvh/sort.cpp
Normal file
190
blender-5.2.0/intern/cycles/bvh/sort.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/sort.h"
|
||||
|
||||
#include "bvh/params.h"
|
||||
#include "bvh/unaligned.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/task.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static const int BVH_SORT_THRESHOLD = 4096;
|
||||
|
||||
struct BVHReferenceCompare {
|
||||
public:
|
||||
int dim;
|
||||
const BVHUnaligned *unaligned_heuristic;
|
||||
const Transform *aligned_space;
|
||||
|
||||
BVHReferenceCompare(const int dim,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: dim(dim), unaligned_heuristic(unaligned_heuristic), aligned_space(aligned_space)
|
||||
{
|
||||
}
|
||||
|
||||
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
|
||||
{
|
||||
return (aligned_space != nullptr) ?
|
||||
unaligned_heuristic->compute_aligned_prim_boundbox(prim, *aligned_space) :
|
||||
prim.bounds();
|
||||
}
|
||||
|
||||
/* Compare two references.
|
||||
*
|
||||
* Returns value is similar to return value of `strcmp()`.
|
||||
*/
|
||||
__forceinline int compare(const BVHReference &ra, const BVHReference &rb) const
|
||||
{
|
||||
BoundBox ra_bounds = get_prim_bounds(ra);
|
||||
BoundBox rb_bounds = get_prim_bounds(rb);
|
||||
const float ca = ra_bounds.min[dim] + ra_bounds.max[dim];
|
||||
const float cb = rb_bounds.min[dim] + rb_bounds.max[dim];
|
||||
|
||||
if (ca < cb) {
|
||||
return -1;
|
||||
}
|
||||
if (ca > cb) {
|
||||
return 1;
|
||||
}
|
||||
if (ra.prim_object() < rb.prim_object()) {
|
||||
return -1;
|
||||
}
|
||||
if (ra.prim_object() > rb.prim_object()) {
|
||||
return 1;
|
||||
}
|
||||
if (ra.prim_index() < rb.prim_index()) {
|
||||
return -1;
|
||||
}
|
||||
if (ra.prim_index() > rb.prim_index()) {
|
||||
return 1;
|
||||
}
|
||||
if (ra.prim_type() < rb.prim_type()) {
|
||||
return -1;
|
||||
}
|
||||
if (ra.prim_type() > rb.prim_type()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator()(const BVHReference &ra, const BVHReference &rb)
|
||||
{
|
||||
return (compare(ra, rb) < 0);
|
||||
}
|
||||
};
|
||||
|
||||
static void bvh_reference_sort_threaded(TaskPool *task_pool,
|
||||
BVHReference *data,
|
||||
const int job_start,
|
||||
const int job_end,
|
||||
const BVHReferenceCompare &compare);
|
||||
|
||||
/* Multi-threaded reference sort. */
|
||||
static void bvh_reference_sort_threaded(TaskPool *task_pool,
|
||||
BVHReference *data,
|
||||
const int job_start,
|
||||
const int job_end,
|
||||
const BVHReferenceCompare &compare)
|
||||
{
|
||||
int start = job_start;
|
||||
int end = job_end;
|
||||
bool have_work = (start < end);
|
||||
while (have_work) {
|
||||
const int count = job_end - job_start;
|
||||
if (count < BVH_SORT_THRESHOLD) {
|
||||
/* Number of reference low enough, faster to finish the job
|
||||
* in one thread rather than to spawn more threads.
|
||||
*/
|
||||
sort(data + job_start, data + job_end + 1, compare);
|
||||
break;
|
||||
}
|
||||
/* Single QSort step.
|
||||
* Use median-of-three method for the pivot point.
|
||||
*/
|
||||
int left = start;
|
||||
int right = end;
|
||||
const int center = (left + right) >> 1;
|
||||
if (compare.compare(data[left], data[center]) > 0) {
|
||||
swap(data[left], data[center]);
|
||||
}
|
||||
if (compare.compare(data[left], data[right]) > 0) {
|
||||
swap(data[left], data[right]);
|
||||
}
|
||||
if (compare.compare(data[center], data[right]) > 0) {
|
||||
swap(data[center], data[right]);
|
||||
}
|
||||
swap(data[center], data[right - 1]);
|
||||
const BVHReference median = data[right - 1];
|
||||
do {
|
||||
while (compare.compare(data[left], median) < 0) {
|
||||
++left;
|
||||
}
|
||||
while (compare.compare(data[right], median) > 0) {
|
||||
--right;
|
||||
}
|
||||
if (left <= right) {
|
||||
swap(data[left], data[right]);
|
||||
++left;
|
||||
--right;
|
||||
}
|
||||
} while (left <= right);
|
||||
/* We only create one new task here to reduce downside effects of
|
||||
* latency in TaskScheduler.
|
||||
* So generally current thread keeps working on the left part of the
|
||||
* array, and we create new task for the right side.
|
||||
* However, if there's nothing to be done in the left side of the array
|
||||
* we don't create any tasks and make it so current thread works on the
|
||||
* right side.
|
||||
*/
|
||||
have_work = false;
|
||||
if (left < end) {
|
||||
if (start < right) {
|
||||
task_pool->push([task_pool, data, left, end, compare] {
|
||||
bvh_reference_sort_threaded(task_pool, data, left, end, compare);
|
||||
});
|
||||
}
|
||||
else {
|
||||
start = left;
|
||||
have_work = true;
|
||||
}
|
||||
}
|
||||
if (start < right) {
|
||||
end = right;
|
||||
have_work = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bvh_reference_sort(const int start,
|
||||
const int end,
|
||||
BVHReference *data,
|
||||
const int dim,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
{
|
||||
const int count = end - start;
|
||||
const BVHReferenceCompare compare(dim, unaligned_heuristic, aligned_space);
|
||||
if (count < BVH_SORT_THRESHOLD) {
|
||||
/* It is important to not use any mutex if array is small enough,
|
||||
* otherwise we end up in situation when we're going to sleep far
|
||||
* too often.
|
||||
*/
|
||||
sort(data + start, data + end, compare);
|
||||
}
|
||||
else {
|
||||
TaskPool task_pool;
|
||||
bvh_reference_sort_threaded(&task_pool, data, start, end - 1, compare);
|
||||
task_pool.wait_work();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
23
blender-5.2.0/intern/cycles/bvh/sort.h
Normal file
23
blender-5.2.0/intern/cycles/bvh/sort.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHReference;
|
||||
class BVHUnaligned;
|
||||
struct Transform;
|
||||
|
||||
void bvh_reference_sort(const int start,
|
||||
const int end,
|
||||
BVHReference *data,
|
||||
const int dim,
|
||||
const BVHUnaligned *unaligned_heuristic = nullptr,
|
||||
const Transform *aligned_space = nullptr);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
576
blender-5.2.0/intern/cycles/bvh/split.cpp
Normal file
576
blender-5.2.0/intern/cycles/bvh/split.cpp
Normal file
@@ -0,0 +1,576 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#include "bvh/split.h"
|
||||
|
||||
#include "bvh/build.h"
|
||||
#include "bvh/sort.h"
|
||||
|
||||
#include "scene/hair.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/pointcloud.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Object Split */
|
||||
|
||||
BVHObjectSplit::BVHObjectSplit(BVHBuild *builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const float nodeSAH,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: sah(FLT_MAX),
|
||||
dim(0),
|
||||
num_left(0),
|
||||
left_bounds(BoundBox::empty),
|
||||
right_bounds(BoundBox::empty),
|
||||
storage_(storage),
|
||||
references_(&references),
|
||||
unaligned_heuristic_(unaligned_heuristic),
|
||||
aligned_space_(aligned_space)
|
||||
{
|
||||
const BVHReference *ref_ptr = &references_->at(range.start());
|
||||
float min_sah = FLT_MAX;
|
||||
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
/* Sort references. */
|
||||
bvh_reference_sort(range.start(),
|
||||
range.end(),
|
||||
&references_->at(0),
|
||||
dim,
|
||||
unaligned_heuristic_,
|
||||
aligned_space_);
|
||||
|
||||
// Resize must be called after every bvh_reference_sort, as sorting may use a task pool for
|
||||
// large ranges. This may cause another BVHObjectSplit to use and resize the storage on the
|
||||
// same thread.
|
||||
storage_->right_bounds.resize(range.size());
|
||||
|
||||
/* sweep right to left and determine bounds. */
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
for (int i = range.size() - 1; i > 0; i--) {
|
||||
const BoundBox prim_bounds = get_prim_bounds(ref_ptr[i]);
|
||||
right_bounds.grow(prim_bounds);
|
||||
storage_->right_bounds[i - 1] = right_bounds;
|
||||
}
|
||||
|
||||
/* sweep left to right and select lowest SAH. */
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
|
||||
for (int i = 1; i < range.size(); i++) {
|
||||
const BoundBox prim_bounds = get_prim_bounds(ref_ptr[i - 1]);
|
||||
left_bounds.grow(prim_bounds);
|
||||
right_bounds = storage_->right_bounds[i - 1];
|
||||
|
||||
const float sah = nodeSAH + left_bounds.safe_area() * builder->params.primitive_cost(i) +
|
||||
right_bounds.safe_area() *
|
||||
builder->params.primitive_cost(range.size() - i);
|
||||
|
||||
if (sah < min_sah) {
|
||||
min_sah = sah;
|
||||
|
||||
this->sah = sah;
|
||||
this->dim = dim;
|
||||
this->num_left = i;
|
||||
this->left_bounds = left_bounds;
|
||||
this->right_bounds = right_bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHObjectSplit::split(BVHRange &left, BVHRange &right, const BVHRange &range)
|
||||
{
|
||||
assert(!references_->empty());
|
||||
/* sort references according to split */
|
||||
bvh_reference_sort(range.start(),
|
||||
range.end(),
|
||||
&references_->at(0),
|
||||
this->dim,
|
||||
unaligned_heuristic_,
|
||||
aligned_space_);
|
||||
|
||||
BoundBox effective_left_bounds;
|
||||
BoundBox effective_right_bounds;
|
||||
const int num_right = range.size() - this->num_left;
|
||||
if (aligned_space_ == nullptr) {
|
||||
effective_left_bounds = left_bounds;
|
||||
effective_right_bounds = right_bounds;
|
||||
}
|
||||
else {
|
||||
effective_left_bounds = BoundBox::empty;
|
||||
effective_right_bounds = BoundBox::empty;
|
||||
for (int i = 0; i < this->num_left; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(range.start() + i).bounds();
|
||||
effective_left_bounds.grow(prim_boundbox);
|
||||
}
|
||||
for (int i = 0; i < num_right; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(range.start() + this->num_left + i).bounds();
|
||||
effective_right_bounds.grow(prim_boundbox);
|
||||
}
|
||||
}
|
||||
|
||||
/* split node ranges */
|
||||
left = BVHRange(effective_left_bounds, range.start(), this->num_left);
|
||||
right = BVHRange(effective_right_bounds, left.end(), num_right);
|
||||
}
|
||||
|
||||
/* Spatial Split */
|
||||
|
||||
BVHSpatialSplit::BVHSpatialSplit(const BVHBuild &builder,
|
||||
BVHSpatialStorage *storage,
|
||||
const BVHRange &range,
|
||||
vector<BVHReference> &references,
|
||||
const float nodeSAH,
|
||||
const BVHUnaligned *unaligned_heuristic,
|
||||
const Transform *aligned_space)
|
||||
: sah(FLT_MAX),
|
||||
dim(0),
|
||||
pos(0.0f),
|
||||
storage_(storage),
|
||||
references_(&references),
|
||||
unaligned_heuristic_(unaligned_heuristic),
|
||||
aligned_space_(aligned_space)
|
||||
{
|
||||
/* initialize bins. */
|
||||
BoundBox range_bounds;
|
||||
if (aligned_space == nullptr) {
|
||||
range_bounds = range.bounds();
|
||||
}
|
||||
else {
|
||||
range_bounds = unaligned_heuristic->compute_aligned_boundbox(
|
||||
range, &references_->at(0), *aligned_space);
|
||||
}
|
||||
|
||||
float3 origin = range_bounds.min;
|
||||
float3 binSize = (range_bounds.max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
|
||||
const float3 invBinSize = safe_divide(make_float3(1.0f), binSize);
|
||||
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
for (int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
|
||||
BVHSpatialBin &bin = storage_->bins[dim][i];
|
||||
|
||||
bin.bounds = BoundBox::empty;
|
||||
bin.enter = 0;
|
||||
bin.exit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* chop references into bins. */
|
||||
for (unsigned int refIdx = range.start(); refIdx < range.end(); refIdx++) {
|
||||
const BVHReference &ref = references_->at(refIdx);
|
||||
const BoundBox prim_bounds = get_prim_bounds(ref);
|
||||
const float3 firstBinf = (prim_bounds.min - origin) * invBinSize;
|
||||
const float3 lastBinf = (prim_bounds.max - origin) * invBinSize;
|
||||
int3 firstBin = make_int3((int)firstBinf.x, (int)firstBinf.y, (int)firstBinf.z);
|
||||
int3 lastBin = make_int3((int)lastBinf.x, (int)lastBinf.y, (int)lastBinf.z);
|
||||
|
||||
firstBin = clamp(firstBin, 0, BVHParams::NUM_SPATIAL_BINS - 1);
|
||||
lastBin = clamp(lastBin, firstBin, BVHParams::NUM_SPATIAL_BINS - 1);
|
||||
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
BVHReference currRef(
|
||||
get_prim_bounds(ref), ref.prim_index(), ref.prim_object(), ref.prim_type());
|
||||
|
||||
for (int i = firstBin[dim]; i < lastBin[dim]; i++) {
|
||||
BVHReference leftRef;
|
||||
BVHReference rightRef;
|
||||
|
||||
split_reference(
|
||||
builder, leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
|
||||
storage_->bins[dim][i].bounds.grow(leftRef.bounds());
|
||||
currRef = rightRef;
|
||||
}
|
||||
|
||||
storage_->bins[dim][lastBin[dim]].bounds.grow(currRef.bounds());
|
||||
storage_->bins[dim][firstBin[dim]].enter++;
|
||||
storage_->bins[dim][lastBin[dim]].exit++;
|
||||
}
|
||||
}
|
||||
|
||||
/* select best split plane. */
|
||||
storage_->right_bounds.resize(BVHParams::NUM_SPATIAL_BINS);
|
||||
for (int dim = 0; dim < 3; dim++) {
|
||||
/* sweep right to left and determine bounds. */
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
for (int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
|
||||
right_bounds.grow(storage_->bins[dim][i].bounds);
|
||||
storage_->right_bounds[i - 1] = right_bounds;
|
||||
}
|
||||
|
||||
/* sweep left to right and select lowest SAH. */
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
int leftNum = 0;
|
||||
int rightNum = range.size();
|
||||
|
||||
for (int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
|
||||
left_bounds.grow(storage_->bins[dim][i - 1].bounds);
|
||||
leftNum += storage_->bins[dim][i - 1].enter;
|
||||
rightNum -= storage_->bins[dim][i - 1].exit;
|
||||
|
||||
const float sah = nodeSAH +
|
||||
left_bounds.safe_area() * builder.params.primitive_cost(leftNum) +
|
||||
storage_->right_bounds[i - 1].safe_area() *
|
||||
builder.params.primitive_cost(rightNum);
|
||||
|
||||
if (sah < this->sah) {
|
||||
this->sah = sah;
|
||||
this->dim = dim;
|
||||
this->pos = origin[dim] + binSize[dim] * (float)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split(BVHBuild *builder,
|
||||
BVHRange &left,
|
||||
BVHRange &right,
|
||||
const BVHRange &range)
|
||||
{
|
||||
/* Categorize references and compute bounds.
|
||||
*
|
||||
* Left-hand side: [left_start, left_end[
|
||||
* Uncategorized/split: [left_end, right_start[
|
||||
* Right-hand side: [right_start, refs.size()[ */
|
||||
|
||||
vector<BVHReference> &refs = *references_;
|
||||
const int left_start = range.start();
|
||||
int left_end = left_start;
|
||||
int right_start = range.end();
|
||||
int right_end = range.end();
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
|
||||
for (int i = left_end; i < right_start; i++) {
|
||||
BoundBox prim_bounds = get_prim_bounds(refs[i]);
|
||||
if (prim_bounds.max[this->dim] <= this->pos) {
|
||||
/* entirely on the left-hand side */
|
||||
left_bounds.grow(prim_bounds);
|
||||
swap(refs[i], refs[left_end++]);
|
||||
}
|
||||
else if (prim_bounds.min[this->dim] >= this->pos) {
|
||||
/* entirely on the right-hand side */
|
||||
right_bounds.grow(prim_bounds);
|
||||
swap(refs[i--], refs[--right_start]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Duplicate or unsplit references intersecting both sides.
|
||||
*
|
||||
* Duplication happens into a temporary pre-allocated vector in order to
|
||||
* reduce number of `memmove()` calls happening in `vector.insert()`.
|
||||
*/
|
||||
vector<BVHReference> &new_refs = storage_->new_references;
|
||||
new_refs.clear();
|
||||
new_refs.reserve(right_start - left_end);
|
||||
while (left_end < right_start) {
|
||||
/* split reference. */
|
||||
const BVHReference curr_ref(get_prim_bounds(refs[left_end]),
|
||||
refs[left_end].prim_index(),
|
||||
refs[left_end].prim_object(),
|
||||
refs[left_end].prim_type());
|
||||
BVHReference lref;
|
||||
BVHReference rref;
|
||||
split_reference(*builder, lref, rref, curr_ref, this->dim, this->pos);
|
||||
|
||||
/* compute SAH for duplicate/unsplit candidates. */
|
||||
BoundBox lub = left_bounds; // Unsplit to left: new left-hand bounds.
|
||||
BoundBox rub = right_bounds; // Unsplit to right: new right-hand bounds.
|
||||
BoundBox ldb = left_bounds; // Duplicate: new left-hand bounds.
|
||||
BoundBox rdb = right_bounds; // Duplicate: new right-hand bounds.
|
||||
|
||||
lub.grow(curr_ref.bounds());
|
||||
rub.grow(curr_ref.bounds());
|
||||
ldb.grow(lref.bounds());
|
||||
rdb.grow(rref.bounds());
|
||||
|
||||
const float lac = builder->params.primitive_cost(left_end - left_start);
|
||||
const float rac = builder->params.primitive_cost(right_end - right_start);
|
||||
const float lbc = builder->params.primitive_cost(left_end - left_start + 1);
|
||||
const float rbc = builder->params.primitive_cost(right_end - right_start + 1);
|
||||
|
||||
const float unsplitLeftSAH = lub.safe_area() * lbc + right_bounds.safe_area() * rac;
|
||||
const float unsplitRightSAH = left_bounds.safe_area() * lac + rub.safe_area() * rbc;
|
||||
const float duplicateSAH = ldb.safe_area() * lbc + rdb.safe_area() * rbc;
|
||||
const float minSAH = min(min(unsplitLeftSAH, unsplitRightSAH), duplicateSAH);
|
||||
|
||||
if (minSAH == unsplitLeftSAH) {
|
||||
/* unsplit to left */
|
||||
left_bounds = lub;
|
||||
left_end++;
|
||||
}
|
||||
else if (minSAH == unsplitRightSAH) {
|
||||
/* unsplit to right */
|
||||
right_bounds = rub;
|
||||
swap(refs[left_end], refs[--right_start]);
|
||||
}
|
||||
else {
|
||||
/* duplicate */
|
||||
left_bounds = ldb;
|
||||
right_bounds = rdb;
|
||||
refs[left_end++] = lref;
|
||||
new_refs.push_back(rref);
|
||||
right_end++;
|
||||
}
|
||||
}
|
||||
/* Insert duplicated references into actual array in one go. */
|
||||
if (!new_refs.empty()) {
|
||||
refs.insert(refs.begin() + (right_end - new_refs.size()), new_refs.begin(), new_refs.end());
|
||||
}
|
||||
if (aligned_space_ != nullptr) {
|
||||
left_bounds = right_bounds = BoundBox::empty;
|
||||
for (int i = left_start; i < left_end - left_start; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(i).bounds();
|
||||
left_bounds.grow(prim_boundbox);
|
||||
}
|
||||
for (int i = right_start; i < right_end - right_start; ++i) {
|
||||
const BoundBox prim_boundbox = references_->at(i).bounds();
|
||||
right_bounds.grow(prim_boundbox);
|
||||
}
|
||||
}
|
||||
left = BVHRange(left_bounds, left_start, left_end - left_start);
|
||||
right = BVHRange(right_bounds, right_start, right_end - right_start);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_triangle_primitive(const Mesh *mesh,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
const Mesh::Triangle t = mesh->get_triangle(prim_index);
|
||||
const packed_float3 *verts = mesh->get_position();
|
||||
float3 v1 = tfm ? transform_point(tfm, verts[t.v[2]]) : float3(verts[t.v[2]]);
|
||||
v1 = get_unaligned_point(v1);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float3 v0 = v1;
|
||||
const int vindex = t.v[i];
|
||||
v1 = tfm ? transform_point(tfm, verts[vindex]) : float3(verts[vindex]);
|
||||
v1 = get_unaligned_point(v1);
|
||||
const float v0p = v0[dim];
|
||||
const float v1p = v1[dim];
|
||||
|
||||
/* insert vertex to the boxes it belongs to. */
|
||||
if (v0p <= pos) {
|
||||
left_bounds.grow(v0);
|
||||
}
|
||||
|
||||
if (v0p >= pos) {
|
||||
right_bounds.grow(v0);
|
||||
}
|
||||
|
||||
/* edge intersects the plane => insert intersection to both boxes. */
|
||||
if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
|
||||
const float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
left_bounds.grow(t);
|
||||
right_bounds.grow(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_curve_primitive(const Hair *hair,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int segment_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
/* curve split: NOTE - Currently ignores curve width and needs to be fixed. */
|
||||
const Hair::Curve curve = hair->get_curve(prim_index);
|
||||
const int k0 = curve.first_key + segment_index;
|
||||
const int k1 = k0 + 1;
|
||||
const packed_float3 *curve_keys = hair->get_position();
|
||||
float3 v0 = curve_keys[k0];
|
||||
float3 v1 = curve_keys[k1];
|
||||
|
||||
if (tfm != nullptr) {
|
||||
v0 = transform_point(tfm, v0);
|
||||
v1 = transform_point(tfm, v1);
|
||||
}
|
||||
v0 = get_unaligned_point(v0);
|
||||
v1 = get_unaligned_point(v1);
|
||||
|
||||
const float v0p = v0[dim];
|
||||
const float v1p = v1[dim];
|
||||
|
||||
/* insert vertex to the boxes it belongs to. */
|
||||
if (v0p <= pos) {
|
||||
left_bounds.grow(v0);
|
||||
}
|
||||
|
||||
if (v0p >= pos) {
|
||||
right_bounds.grow(v0);
|
||||
}
|
||||
|
||||
if (v1p <= pos) {
|
||||
left_bounds.grow(v1);
|
||||
}
|
||||
|
||||
if (v1p >= pos) {
|
||||
right_bounds.grow(v1);
|
||||
}
|
||||
|
||||
/* edge intersects the plane => insert intersection to both boxes. */
|
||||
if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
|
||||
const float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
left_bounds.grow(t);
|
||||
right_bounds.grow(t);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_point_primitive(const PointCloud *pointcloud,
|
||||
const Transform *tfm,
|
||||
const int prim_index,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
/* No real splitting support for points, assume they are small enough for it
|
||||
* not to matter. */
|
||||
float3 point = float3(pointcloud->get_position()[prim_index]);
|
||||
const float radius = pointcloud->get_radius()[prim_index];
|
||||
|
||||
if (tfm != nullptr) {
|
||||
point = transform_point(tfm, point);
|
||||
}
|
||||
point = get_unaligned_point(point);
|
||||
|
||||
if (point[dim] - radius <= pos) {
|
||||
left_bounds.grow(point, radius);
|
||||
}
|
||||
|
||||
if (point[dim] + radius >= pos) {
|
||||
right_bounds.grow(point, radius);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_triangle_reference(const BVHReference &ref,
|
||||
const Mesh *mesh,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
split_triangle_primitive(mesh, nullptr, ref.prim_index(), dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_curve_reference(const BVHReference &ref,
|
||||
const Hair *hair,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
split_curve_primitive(hair,
|
||||
nullptr,
|
||||
ref.prim_index(),
|
||||
PRIMITIVE_UNPACK_SEGMENT(ref.prim_type()),
|
||||
dim,
|
||||
pos,
|
||||
left_bounds,
|
||||
right_bounds);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_point_reference(const BVHReference &ref,
|
||||
const PointCloud *pointcloud,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
split_point_primitive(
|
||||
pointcloud, nullptr, ref.prim_index(), dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_object_reference(const Object *object,
|
||||
const int dim,
|
||||
const float pos,
|
||||
BoundBox &left_bounds,
|
||||
BoundBox &right_bounds)
|
||||
{
|
||||
Geometry *geom = object->get_geometry();
|
||||
|
||||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
for (int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) {
|
||||
split_triangle_primitive(
|
||||
mesh, &object->get_tfm(), tri_idx, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
for (int curve_idx = 0; curve_idx < hair->num_curves(); ++curve_idx) {
|
||||
const Hair::Curve curve = hair->get_curve(curve_idx);
|
||||
for (int segment_idx = 0; segment_idx < curve.num_keys - 1; ++segment_idx) {
|
||||
split_curve_primitive(
|
||||
hair, &object->get_tfm(), curve_idx, segment_idx, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (geom->is_pointcloud()) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
|
||||
for (int point_idx = 0; point_idx < pointcloud->num_points(); ++point_idx) {
|
||||
split_point_primitive(
|
||||
pointcloud, &object->get_tfm(), point_idx, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHSpatialSplit::split_reference(const BVHBuild &builder,
|
||||
BVHReference &left,
|
||||
BVHReference &right,
|
||||
const BVHReference &ref,
|
||||
const int dim,
|
||||
const float pos)
|
||||
{
|
||||
/* Initialize bounding-boxes. */
|
||||
BoundBox left_bounds = BoundBox::empty;
|
||||
BoundBox right_bounds = BoundBox::empty;
|
||||
|
||||
/* loop over vertices/edges. */
|
||||
const Object *ob = builder.objects[ref.prim_object()];
|
||||
|
||||
if (ref.prim_type() & PRIMITIVE_TRIANGLE) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->get_geometry());
|
||||
split_triangle_reference(ref, mesh, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
else if (ref.prim_type() & PRIMITIVE_CURVE) {
|
||||
Hair *hair = static_cast<Hair *>(ob->get_geometry());
|
||||
split_curve_reference(ref, hair, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
else if (ref.prim_type() & PRIMITIVE_POINT) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(ob->get_geometry());
|
||||
split_point_reference(ref, pointcloud, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
else {
|
||||
split_object_reference(ob, dim, pos, left_bounds, right_bounds);
|
||||
}
|
||||
|
||||
/* intersect with original bounds. */
|
||||
left_bounds.max[dim] = pos;
|
||||
right_bounds.min[dim] = pos;
|
||||
|
||||
left_bounds.intersect(ref.bounds());
|
||||
right_bounds.intersect(ref.bounds());
|
||||
|
||||
/* set references */
|
||||
left = BVHReference(left_bounds, ref.prim_index(), ref.prim_object(), ref.prim_type());
|
||||
right = BVHReference(right_bounds, ref.prim_index(), ref.prim_object(), ref.prim_type());
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user