Merge remote-tracking branch 'origin/master' into temp_bmesh_multires

Also redid vcol boundary tool icon, and made a little icon for sculpt
color paint.
This commit is contained in:
Joseph Eagar 2020-12-21 01:08:24 -08:00
commit b3fff9b07a
916 changed files with 28116 additions and 10114 deletions

View File

@ -347,16 +347,21 @@ if(UNIX AND NOT APPLE)
endif()
option(WITH_PYTHON_INSTALL "Copy system python into the blender install folder" ON)
if((WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE) OR WITH_MOD_FLUID)
option(WITH_PYTHON_NUMPY "Include NumPy in Blender (used by Audaspace and Mantaflow)" ON)
endif()
if(WIN32 OR APPLE)
# Windows and macOS have this bundled with Python libraries.
elseif(WITH_PYTHON_INSTALL OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE))
elseif(WITH_PYTHON_INSTALL OR WITH_PYTHON_NUMPY)
set(PYTHON_NUMPY_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'numpy' module")
mark_as_advanced(PYTHON_NUMPY_PATH)
set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module")
set(PYTHON_NUMPY_INCLUDE_DIRS "" CACHE PATH "Path to the include directory of the NumPy module")
mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS)
endif()
if(WITH_PYTHON_INSTALL)
option(WITH_PYTHON_INSTALL_NUMPY "Copy system numpy into the blender install folder" ON)
option(WITH_PYTHON_INSTALL_NUMPY "Copy system NumPy into the blender install folder" ON)
if(UNIX AND NOT APPLE)
option(WITH_PYTHON_INSTALL_REQUESTS "Copy system requests into the blender install folder" ON)
@ -1621,19 +1626,16 @@ if(WITH_PYTHON)
if(WIN32 OR APPLE)
# Windows and macOS have this bundled with Python libraries.
elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE))
elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR WITH_PYTHON_NUMPY)
if(("${PYTHON_NUMPY_PATH}" STREQUAL "") OR (${PYTHON_NUMPY_PATH} MATCHES NOTFOUND))
find_python_package(numpy)
unset(PYTHON_NUMPY_INCLUDE_DIRS CACHE)
set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module")
mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS)
find_python_package(numpy "core/include")
endif()
endif()
if(WIN32 OR APPLE)
# pass, we have this in lib/python/site-packages
elseif(WITH_PYTHON_INSTALL_REQUESTS)
find_python_package(requests)
find_python_package(requests "")
endif()
endif()

View File

@ -1139,6 +1139,7 @@ endfunction()
function(find_python_package
package
relative_include_dir
)
string(TOUPPER ${package} _upper_package)
@ -1170,7 +1171,10 @@ function(find_python_package
dist-packages
vendor-packages
NO_DEFAULT_PATH
DOC
"Path to python site-packages or dist-packages containing '${package}' module"
)
mark_as_advanced(PYTHON_${_upper_package}_PATH)
if(NOT EXISTS "${PYTHON_${_upper_package}_PATH}")
message(WARNING
@ -1188,6 +1192,50 @@ function(find_python_package
set(WITH_PYTHON_INSTALL_${_upper_package} OFF PARENT_SCOPE)
else()
message(STATUS "${package} found at '${PYTHON_${_upper_package}_PATH}'")
if(NOT "${relative_include_dir}" STREQUAL "")
set(_relative_include_dir "${package}/${relative_include_dir}")
unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE)
find_path(PYTHON_${_upper_package}_INCLUDE_DIRS
NAMES
"${_relative_include_dir}"
HINTS
"${PYTHON_LIBPATH}/"
"${PYTHON_LIBPATH}/python${PYTHON_VERSION}/"
"${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/"
PATH_SUFFIXES
"site-packages/"
"dist-packages/"
"vendor-packages/"
NO_DEFAULT_PATH
DOC
"Path to python site-packages or dist-packages containing '${package}' module header files"
)
mark_as_advanced(PYTHON_${_upper_package}_INCLUDE_DIRS)
if(NOT EXISTS "${PYTHON_${_upper_package}_INCLUDE_DIRS}")
message(WARNING
"Python package '${package}' include dir path could not be found in:\n"
"'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/site-packages/${_relative_include_dir}', "
"'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/site-packages/${_relative_include_dir}', "
"'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/dist-packages/${_relative_include_dir}', "
"'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/dist-packages/${_relative_include_dir}', "
"'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/vendor-packages/${_relative_include_dir}', "
"'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/vendor-packages/${_relative_include_dir}', "
"\n"
"The 'WITH_PYTHON_${_upper_package}' option will be disabled.\n"
"The build will be usable, only add-ons that depend on this package won't be functional."
)
set(WITH_PYTHON_${_upper_package} OFF PARENT_SCOPE)
else()
set(_temp "${PYTHON_${_upper_package}_INCLUDE_DIRS}/${package}/${relative_include_dir}")
unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE)
set(PYTHON_${_upper_package}_INCLUDE_DIRS "${_temp}"
CACHE PATH "Path to the include directory of the ${package} module")
message(STATUS "${package} include files found at '${PYTHON_${_upper_package}_INCLUDE_DIRS}'")
endif()
endif()
endif()
endif()
endfunction()

View File

@ -739,7 +739,7 @@ if(WINDOWS_PYTHON_DEBUG)
string(REPLACE "/" "\\" _group_path "${_source_path}")
source_group("${_group_path}" FILES "${_source}")
endforeach()
# If the user scripts env var is set, include scripts from there otherwise
# include user scripts in the profile folder.
if(DEFINED ENV{BLENDER_USER_SCRIPTS})
@ -750,7 +750,7 @@ if(WINDOWS_PYTHON_DEBUG)
# Include the user scripts from the profile folder in the blender_python_user_scripts project.
set(USER_SCRIPTS_ROOT "$ENV{appdata}/blender foundation/blender/${BLENDER_VERSION}/scripts")
endif()
file(TO_CMAKE_PATH ${USER_SCRIPTS_ROOT} USER_SCRIPTS_ROOT)
FILE(GLOB_RECURSE inFiles "${USER_SCRIPTS_ROOT}/*.*" )
ADD_CUSTOM_TARGET(blender_python_user_scripts SOURCES ${inFiles})

View File

@ -121,6 +121,10 @@
* \ingroup editors
*/
/** \defgroup edasset asset
* \ingroup editors
*/
/** \defgroup edcurve curve
* \ingroup editors
*/

View File

@ -0,0 +1,30 @@
"""
.. note::
Properties defined at run-time store the values of the properties as custom-properties.
This method checks if the underlying data exists, causing the property to be considered *set*.
A common pattern for operators is to calculate a value for the properties
that have not had their values explicitly set by the caller
(where the caller could be a key-binding, menu-items or Python script for example).
In the case of executing operators multiple times, values are re-used from the previous execution.
For example: subdividing a mesh with a smooth value of 1.0 will keep using
that value on subsequent calls to subdivision, unless the operator is called with
that property set to a different value.
This behavior can be disabled using the ``SKIP_SAVE`` option when the property is declared (see: :mod:`bpy.props`).
The ``ghost`` argument allows detecting how a value from a previous execution is handled.
- When true: The property is considered unset even if the value from a previous call is used.
- When false: The existence of any values causes ``is_property_set`` to return true.
While this argument should typically be omitted, there are times when
it's important to know if a value is anything besides the default.
For example, the previous value may have been scaled by the scene's unit scale.
In this case scaling the value multiple times would cause problems, so the ``ghost`` argument should be false.
"""

View File

@ -551,7 +551,7 @@ def example_extract_docstring(filepath):
file.close()
return "", 0
for line in file.readlines():
for line in file:
line_no += 1
if line.startswith('"""'):
break
@ -559,6 +559,13 @@ def example_extract_docstring(filepath):
text.append(line.rstrip())
line_no += 1
# Skip over blank lines so the Python code doesn't have blank lines at the top.
for line in file:
if line.strip():
break
line_no += 1
file.close()
return "\n".join(text), line_no

4
extern/README vendored Normal file
View File

@ -0,0 +1,4 @@
When updating a library remember to:
* Update the README.blender with the corresponding version.
* Update the THIRD-PARTY-LICENSE.txt document

5
extern/audaspace/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: Audaspace
URL: https://audaspace.github.io/
License: Apache 2.0
Upstream version: 1.3 (Last Release)
Local modifications: None

View File

@ -24,6 +24,6 @@ set(JACK_FOUND ${WITH_JACK})
set(LIBSNDFILE_FOUND ${WITH_CODEC_SNDFILE})
set(OPENAL_FOUND ${WITH_OPENAL})
set(PYTHONLIBS_FOUND TRUE)
set(NUMPY_FOUND TRUE)
set(NUMPY_FOUND ${WITH_PYTHON_NUMPY})
set(NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_INCLUDE_DIRS})
set(SDL_FOUND ${WITH_SDL})

5
extern/bullet2/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: Bullet Continuous Collision Detection and Physics Library
URL: http://bulletphysics.org
License: zlib
Upstream version: 3.07
Local modifications: Fixed inertia

View File

@ -1,4 +1,5 @@
Project: Ceres Solver
URL: http://ceres-solver.org/
License: BSD 3-Clause
Upstream version 2.0.0
Local modifications: None

View File

@ -1,5 +1,5 @@
Project: Curve-Fit-nD
URL: https://github.com/ideasman42/curve-fit-nd
License: BSD 3-Clause
Upstream version: Unknown (Last Release)
Upstream version: ddcd5bd (Last Release)
Local modifications: None

5
extern/draco/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: Draco
URL: https://google.github.io/draco/
License: Apache 2.0
Upstream version: 1.3.6
Local modifications: None

View File

@ -268,4 +268,3 @@ set(INC
)
blender_add_lib(draco "${SRC}" "${INC}" "" "${LIB}")

View File

@ -1,6 +1,6 @@
Project: Google Flags
URL: https://github.com/gflags/gflags
License: New BSD
License: BSD 3-Clause
Upstream version: 2.2.1 (46f73f88b18)
Local modifications:

73
extern/glew-es/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,73 @@
The OpenGL Extension Wrangler Library
Copyright (C) 2002-2007, Milan Ikits <milan ikits[]ieee org>
Copyright (C) 2002-2007, Marcelo E. Magallon <mmagallo[]debian org>
Copyright (C) 2002, Lev Povalahev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 OWNER OR CONTRIBUTORS 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.
Mesa 3-D graphics library
Version: 7.0
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) 2007 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Materials.
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

5
extern/glew-es/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: The OpenGL Extension Wrangler Library
URL: http://glew.sourceforge.net/
License: Check LICENSE.txt
Upstream version: 2.0.0
Local modifications: None

73
extern/glew/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,73 @@
The OpenGL Extension Wrangler Library
Copyright (C) 2002-2007, Milan Ikits <milan ikits[]ieee org>
Copyright (C) 2002-2007, Marcelo E. Magallon <mmagallo[]debian org>
Copyright (C) 2002, Lev Povalahev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 OWNER OR CONTRIBUTORS 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.
Mesa 3-D graphics library
Version: 7.0
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) 2007 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Materials.
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

5
extern/glew/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: The OpenGL Extension Wrangler Library
URL: http://glew.sourceforge.net/
License: Check LICENSE.txt
Upstream version: 2.0.0
Local modifications: None

View File

@ -1,6 +1,6 @@
Project: Google Logging
URL: https://github.com/google/glog
License: New BSD
License: BSD 3-Clause
Upstream version: 0.4.0, 96a2f23dca4
Local modifications:
* Added per-platform config.h files so no configuration-time

View File

@ -1,5 +1,5 @@
Project: Google C++ Testing Framework
URL: https://github.com/google/googletest
License: New BSD
License: BSD 3-Clause
Upstream version: 1.10.0 (703bd9caab5)
Local modifications: None

View File

@ -1,5 +1,5 @@
Project: Google C++ Testing Framework
URL: https://github.com/google/googletest
License: New BSD
License: BSD 3-Clause
Upstream version: 1.10.0 (703bd9caab5)
Local modifications: None

5
extern/lzma/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: LZMA SDK
URL: https://www.7-zip.org/sdk.html
License: Public Domain
Upstream version: 4.65
Local modifications: None

5
extern/lzo/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: miniLZO - mini subset of the LZO real-time data compression librar
URL: http://www.oberhumer.com/opensource/lzo/
License: GPLv2+
Upstream version: 2.08
Local modifications: None

View File

@ -85,7 +85,7 @@ if(WIN32)
add_definitions(-D_USE_MATH_DEFINES)
endif()
if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
add_definitions(-DNUMPY=1)
endif()
@ -109,7 +109,7 @@ set(INC_SYS
${ZLIB_INCLUDE_DIRS}
)
if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
list(APPEND INC_SYS
${PYTHON_NUMPY_INCLUDE_DIRS}
)
@ -255,8 +255,7 @@ if(WITH_MANTA_DEPENDENCIES)
${MANTA_DEP}/cnpy/cnpy.h
)
endif()
if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
list(APPEND SRC
${MANTA_PP}/plugin/numpyconvert.cpp
${MANTA_PP}/plugin/tfplugins.cpp

5
extern/mantaflow/README.blender vendored Normal file
View File

@ -0,0 +1,5 @@
Project: Mantaflow
URL: http://mantaflow.com/
License: Apache 2.0
Upstream version: 0.13
Local modifications: None

View File

@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
elif entry.type == 'CPU':
cpu_devices.append(entry)
# Extend all GPU devices with CPU.
if compute_device_type in {'CUDA', 'OPENCL'}:
if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}:
devices.extend(cpu_devices)
return devices
@ -1620,11 +1620,6 @@ class CyclesPreferences(bpy.types.AddonPreferences):
for device in devices:
box.prop(device, "use", text=device.name)
if device_type == 'OPTIX':
col = box.column(align=True)
col.label(text="OptiX support is experimental", icon='INFO')
col.label(text="Not all Cycles features are supported yet", icon='BLANK1')
def draw_impl(self, layout, context):
row = layout.row()
row.prop(self, "compute_device_type", expand=True)

View File

@ -1148,7 +1148,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
split = layout.split(factor=0.65)
if ob:
split.template_ID(ob, "active_material", new="material.new")
split.template_ID(ob, "active_material", new="material.new", duplicate="material.duplicate")
row = split.row()
if slot:
@ -1822,10 +1822,6 @@ class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel):
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'CYCLES'}
@classmethod
def poll(cls, context):
return CyclesButtonsPanel.poll(context) and not use_optix(context)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@ -1836,6 +1832,9 @@ class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel):
cbk = scene.render.bake
rd = scene.render
if use_optix(context):
layout.label(text="Baking is performed using CUDA instead of OptiX", icon='INFO')
if rd.use_bake_multires:
layout.operator("object.bake_image", icon='RENDER_STILL')
layout.prop(rd, "use_bake_multires")

View File

@ -90,8 +90,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
mask |= DEVICE_MASK_CUDA;
}
else if (compute_device == COMPUTE_DEVICE_OPTIX) {
/* Cannot use CPU and OptiX device at the same time right now, so replace mask. */
mask = DEVICE_MASK_OPTIX;
mask |= DEVICE_MASK_OPTIX;
}
else if (compute_device == COMPUTE_DEVICE_OPENCL) {
mask |= DEVICE_MASK_OPENCL;

View File

@ -1064,6 +1064,8 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me
/* update original sockets */
mesh->clear_non_sockets();
for (const SocketType &socket : new_mesh.type->inputs) {
/* Those sockets are updated in sync_object, so do not modify them. */
if (socket.name == "use_motion_blur" || socket.name == "motion_steps" ||

View File

@ -25,6 +25,7 @@ set(SRC
bvh_binning.cpp
bvh_build.cpp
bvh_embree.cpp
bvh_multi.cpp
bvh_node.cpp
bvh_optix.cpp
bvh_sort.cpp
@ -38,6 +39,7 @@ set(SRC_HEADERS
bvh_binning.h
bvh_build.h
bvh_embree.h
bvh_multi.h
bvh_node.h
bvh_optix.h
bvh_params.h

View File

@ -17,17 +17,11 @@
#include "bvh/bvh.h"
#include "render/hair.h"
#include "render/mesh.h"
#include "render/object.h"
#include "bvh/bvh2.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_embree.h"
#include "bvh/bvh_node.h"
#include "bvh/bvh_multi.h"
#include "bvh/bvh_optix.h"
#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
@ -38,14 +32,17 @@ CCL_NAMESPACE_BEGIN
const char *bvh_layout_name(BVHLayout layout)
{
switch (layout) {
case BVH_LAYOUT_BVH2:
return "BVH2";
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_MULTI_OPTIX:
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
return "MULTI";
case BVH_LAYOUT_ALL:
return "ALL";
}
@ -76,17 +73,6 @@ BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask s
return (BVHLayout)(1 << widest_allowed_layout_mask);
}
/* Pack Utility */
BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
{
}
int BVHStackEntry::encodeIdx() const
{
return (node->is_leaf()) ? ~idx : idx;
}
/* BVH */
BVH::BVH(const BVHParams &params_,
@ -99,24 +85,27 @@ BVH::BVH(const BVHParams &params_,
BVH *BVH::create(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects,
const Device *device)
Device *device)
{
switch (params.bvh_layout) {
case BVH_LAYOUT_BVH2:
return new BVH2(params, geometry, objects);
case BVH_LAYOUT_EMBREE:
#ifdef WITH_EMBREE
return new BVHEmbree(params, geometry, objects, device);
return new BVHEmbree(params, geometry, objects);
#else
(void)device;
break;
#endif
case BVH_LAYOUT_OPTIX:
#ifdef WITH_OPTIX
return new BVHOptiX(params, geometry, objects);
return new BVHOptiX(params, geometry, objects, device);
#else
(void)device;
break;
#endif
case BVH_LAYOUT_MULTI_OPTIX:
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
return new BVHMulti(params, geometry, objects);
case BVH_LAYOUT_NONE:
case BVH_LAYOUT_ALL:
break;
@ -125,399 +114,4 @@ BVH *BVH::create(const BVHParams &params,
return NULL;
}
/* Building */
void BVH::build(Progress &progress, Stats *)
{
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);
BVHNode *bvh2_root = bvh_build.run();
if (progress.get_cancel()) {
if (bvh2_root != NULL) {
bvh2_root->deleteSubtree();
}
return;
}
/* BVH builder returns tree in a binary mode (with two children per inner
* node. Need to adopt that for a wider BVH implementations. */
BVHNode *root = widen_children_nodes(bvh2_root);
if (root != bvh2_root) {
bvh2_root->deleteSubtree();
}
if (progress.get_cancel()) {
if (root != NULL) {
root->deleteSubtree();
}
return;
}
/* pack triangles */
progress.set_substatus("Packing BVH triangles and strands");
pack_primitives();
if (progress.get_cancel()) {
root->deleteSubtree();
return;
}
/* pack nodes */
progress.set_substatus("Packing BVH nodes");
pack_nodes(root);
/* free build nodes */
root->deleteSubtree();
}
/* Refitting */
void BVH::refit(Progress &progress)
{
progress.set_substatus("Packing BVH primitives");
pack_primitives();
if (progress.get_cancel())
return;
progress.set_substatus("Refitting BVH nodes");
refit_nodes();
}
void BVH::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
{
/* Refit range of primitives. */
for (int prim = start; prim < end; prim++) {
int pidx = pack.prim_index[prim];
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_ALL_CURVE) {
/* Curves. */
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
int prim_offset = (params.top_level) ? hair->prim_offset : 0;
Hair::Curve curve = hair->get_curve(pidx - prim_offset);
int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox);
/* Motion curves. */
if (hair->get_use_motion_blur()) {
Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr) {
size_t hair_size = hair->get_curve_keys().size();
size_t steps = hair->get_motion_steps() - 1;
float3 *key_steps = attr->data_float3();
for (size_t i = 0; i < steps; i++)
curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox);
}
}
}
else {
/* Triangles. */
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
const float3 *vpos = &mesh->verts[0];
triangle.bounds_grow(vpos, bbox);
/* Motion triangles. */
if (mesh->use_motion_blur) {
Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr) {
size_t mesh_size = mesh->verts.size();
size_t steps = mesh->motion_steps - 1;
float3 *vert_steps = attr->data_float3();
for (size_t i = 0; i < steps; i++)
triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
}
}
}
}
visibility |= ob->visibility_for_tracing();
}
}
/* Triangles */
void BVH::pack_triangle(int idx, float4 tri_verts[3])
{
int tob = pack.prim_object[idx];
assert(tob >= 0 && tob < objects.size());
const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry());
int tidx = pack.prim_index[idx];
Mesh::Triangle t = mesh->get_triangle(tidx);
const float3 *vpos = &mesh->verts[0];
float3 v0 = vpos[t.v[0]];
float3 v1 = vpos[t.v[1]];
float3 v2 = vpos[t.v[2]];
tri_verts[0] = float3_to_float4(v0);
tri_verts[1] = float3_to_float4(v1);
tri_verts[2] = float3_to_float4(v2);
}
void BVH::pack_primitives()
{
const size_t tidx_size = pack.prim_index.size();
size_t num_prim_triangles = 0;
/* Count number of triangles primitives in BVH. */
for (unsigned int i = 0; i < tidx_size; i++) {
if ((pack.prim_index[i] != -1)) {
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
++num_prim_triangles;
}
}
}
/* Reserve size for arrays. */
pack.prim_tri_index.clear();
pack.prim_tri_index.resize(tidx_size);
pack.prim_tri_verts.clear();
pack.prim_tri_verts.resize(num_prim_triangles * 3);
pack.prim_visibility.clear();
pack.prim_visibility.resize(tidx_size);
/* Fill in all the arrays. */
size_t prim_triangle_index = 0;
for (unsigned int i = 0; i < tidx_size; i++) {
if (pack.prim_index[i] != -1) {
int tob = pack.prim_object[i];
Object *ob = objects[tob];
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]);
pack.prim_tri_index[i] = 3 * prim_triangle_index;
++prim_triangle_index;
}
else {
pack.prim_tri_index[i] = -1;
}
pack.prim_visibility[i] = ob->visibility_for_tracing();
}
else {
pack.prim_tri_index[i] = -1;
pack.prim_visibility[i] = 0;
}
}
}
/* Pack Instances */
void BVH::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 prim_tri_verts_size = pack.prim_tri_verts.size();
size_t pack_prim_index_offset = prim_index_size;
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
size_t pack_nodes_offset = nodes_size;
size_t pack_leaf_nodes_offset = leaf_nodes_size;
size_t object_offset = 0;
foreach (Geometry *geom, geometry) {
BVH *bvh = geom->bvh;
if (geom->need_build_bvh(params.bvh_layout)) {
prim_index_size += bvh->pack.prim_index.size();
prim_tri_verts_size += bvh->pack.prim_tri_verts.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.prim_tri_verts.resize(prim_tri_verts_size);
pack.prim_tri_index.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) {
pack.prim_time.resize(prim_index_size);
}
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL;
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL;
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL;
float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL;
map<Geometry *, int> geometry_map;
/* merge */
foreach (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 */
map<Geometry *, int>::iterator it = geometry_map.find(geom);
if (geometry_map.find(geom) != geometry_map.end()) {
int noffset = it->second;
pack.object_node[object_offset++] = noffset;
continue;
}
BVH *bvh = geom->bvh;
int noffset = nodes_offset;
int noffset_leaf = nodes_leaf_offset;
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()) {
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
int *bvh_prim_index = &bvh->pack.prim_index[0];
int *bvh_prim_type = &bvh->pack.prim_type[0];
uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
for (size_t i = 0; i < bvh_prim_index_size; i++) {
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_prim_index_offset] = -1;
}
else {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
pack_prim_tri_verts_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 != NULL) {
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
}
pack_prim_index_offset++;
}
}
/* Merge triangle vertices data. */
if (bvh->pack.prim_tri_verts.size()) {
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
&bvh->pack.prim_tri_verts[0],
prim_tri_size * sizeof(float4));
pack_prim_tri_verts_offset += prim_tri_size;
}
/* merge nodes */
if (bvh->pack.leaf_nodes.size()) {
int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) {
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[0];
size_t bvh_nodes_size = bvh->pack.nodes.size();
for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
size_t nsize, nsize_bbox;
if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
nsize = BVH_UNALIGNED_NODE_SIZE;
nsize_bbox = 0;
}
else {
nsize = BVH_NODE_SIZE;
nsize_bbox = 0;
}
memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
/* 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.
*/
memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1],
&bvh_nodes[i + nsize_bbox + 1],
sizeof(int4) * (nsize - (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

View File

@ -25,17 +25,16 @@
CCL_NAMESPACE_BEGIN
class Stats;
class BoundBox;
class BVHNode;
class BVHParams;
class Device;
class DeviceScene;
class BVHNode;
struct BVHStackEntry;
class BVHParams;
class BoundBox;
class LeafNode;
class Geometry;
class LeafNode;
class Object;
class Progress;
class Stats;
#define BVH_ALIGN 4096
#define TRI_NODE_SIZE 3
@ -76,13 +75,10 @@ struct PackedBVH {
}
};
enum BVH_TYPE { bvh2 };
/* BVH */
class BVH {
public:
PackedBVH pack;
BVHParams params;
vector<Geometry *> geometry;
vector<Object *> objects;
@ -90,47 +86,15 @@ class BVH {
static BVH *create(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects,
const Device *device);
Device *device);
virtual ~BVH()
{
}
virtual void build(Progress &progress, Stats *stats = NULL);
virtual void copy_to_device(Progress & /*progress*/, DeviceScene * /*dscene*/)
{
}
void refit(Progress &progress);
protected:
BVH(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects);
/* Refit range of primitives. */
void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
/* triangles and strands */
void pack_primitives();
void pack_triangle(int idx, float4 storage[3]);
/* merge instance BVH's */
void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
/* for subclasses to implement */
virtual void pack_nodes(const BVHNode *root) = 0;
virtual void refit_nodes() = 0;
virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0;
};
/* Pack Utility */
struct BVHStackEntry {
const BVHNode *node;
int idx;
BVHStackEntry(const BVHNode *n = 0, int i = 0);
int encodeIdx() const;
};
CCL_NAMESPACE_END

View File

@ -17,14 +17,28 @@
#include "bvh/bvh2.h"
#include "render/hair.h"
#include "render/mesh.h"
#include "render/object.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_node.h"
#include "bvh/bvh_unaligned.h"
#include "util/util_foreach.h"
#include "util/util_progress.h"
CCL_NAMESPACE_BEGIN
BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
{
}
int BVHStackEntry::encodeIdx() const
{
return (node->is_leaf()) ? ~idx : idx;
}
BVH2::BVH2(const BVHParams &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_)
@ -32,6 +46,70 @@ BVH2::BVH2(const BVHParams &params_,
{
}
void BVH2::build(Progress &progress, Stats *)
{
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);
BVHNode *bvh2_root = bvh_build.run();
if (progress.get_cancel()) {
if (bvh2_root != NULL) {
bvh2_root->deleteSubtree();
}
return;
}
/* BVH builder returns tree in a binary mode (with two children per inner
* node. Need to adopt that for a wider BVH implementations. */
BVHNode *root = widen_children_nodes(bvh2_root);
if (root != bvh2_root) {
bvh2_root->deleteSubtree();
}
if (progress.get_cancel()) {
if (root != NULL) {
root->deleteSubtree();
}
return;
}
/* pack triangles */
progress.set_substatus("Packing BVH triangles and strands");
pack_primitives();
if (progress.get_cancel()) {
root->deleteSubtree();
return;
}
/* pack nodes */
progress.set_substatus("Packing BVH nodes");
pack_nodes(root);
/* free build nodes */
root->deleteSubtree();
}
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();
}
BVHNode *BVH2::widen_children_nodes(const BVHNode *root)
{
return const_cast<BVHNode *>(root);
@ -253,7 +331,7 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
const int c0 = data[0].x;
const int c1 = data[0].y;
BVH::refit_primitives(c0, c1, bbox, visibility);
refit_primitives(c0, c1, bbox, visibility);
/* TODO(sergey): De-duplicate with pack_leaf(). */
float4 leaf_data[BVH_NODE_LEAF_SIZE];
@ -292,4 +370,333 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
}
}
/* Refitting */
void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
{
/* Refit range of primitives. */
for (int prim = start; prim < end; prim++) {
int pidx = pack.prim_index[prim];
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_ALL_CURVE) {
/* Curves. */
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
int prim_offset = (params.top_level) ? hair->prim_offset : 0;
Hair::Curve curve = hair->get_curve(pidx - prim_offset);
int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox);
/* Motion curves. */
if (hair->get_use_motion_blur()) {
Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr) {
size_t hair_size = hair->get_curve_keys().size();
size_t steps = hair->get_motion_steps() - 1;
float3 *key_steps = attr->data_float3();
for (size_t i = 0; i < steps; i++)
curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox);
}
}
}
else {
/* Triangles. */
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
const float3 *vpos = &mesh->verts[0];
triangle.bounds_grow(vpos, bbox);
/* Motion triangles. */
if (mesh->use_motion_blur) {
Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr) {
size_t mesh_size = mesh->verts.size();
size_t steps = mesh->motion_steps - 1;
float3 *vert_steps = attr->data_float3();
for (size_t i = 0; i < steps; i++)
triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
}
}
}
}
visibility |= ob->visibility_for_tracing();
}
}
/* Triangles */
void BVH2::pack_triangle(int idx, float4 tri_verts[3])
{
int tob = pack.prim_object[idx];
assert(tob >= 0 && tob < objects.size());
const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry());
int tidx = pack.prim_index[idx];
Mesh::Triangle t = mesh->get_triangle(tidx);
const float3 *vpos = &mesh->verts[0];
float3 v0 = vpos[t.v[0]];
float3 v1 = vpos[t.v[1]];
float3 v2 = vpos[t.v[2]];
tri_verts[0] = float3_to_float4(v0);
tri_verts[1] = float3_to_float4(v1);
tri_verts[2] = float3_to_float4(v2);
}
void BVH2::pack_primitives()
{
const size_t tidx_size = pack.prim_index.size();
size_t num_prim_triangles = 0;
/* Count number of triangles primitives in BVH. */
for (unsigned int i = 0; i < tidx_size; i++) {
if ((pack.prim_index[i] != -1)) {
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
++num_prim_triangles;
}
}
}
/* Reserve size for arrays. */
pack.prim_tri_index.clear();
pack.prim_tri_index.resize(tidx_size);
pack.prim_tri_verts.clear();
pack.prim_tri_verts.resize(num_prim_triangles * 3);
pack.prim_visibility.clear();
pack.prim_visibility.resize(tidx_size);
/* Fill in all the arrays. */
size_t prim_triangle_index = 0;
for (unsigned int i = 0; i < tidx_size; i++) {
if (pack.prim_index[i] != -1) {
int tob = pack.prim_object[i];
Object *ob = objects[tob];
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]);
pack.prim_tri_index[i] = 3 * prim_triangle_index;
++prim_triangle_index;
}
else {
pack.prim_tri_index[i] = -1;
}
pack.prim_visibility[i] = ob->visibility_for_tracing();
}
else {
pack.prim_tri_index[i] = -1;
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 prim_tri_verts_size = pack.prim_tri_verts.size();
size_t pack_prim_index_offset = prim_index_size;
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
size_t pack_nodes_offset = nodes_size;
size_t pack_leaf_nodes_offset = leaf_nodes_size;
size_t object_offset = 0;
foreach (Geometry *geom, geometry) {
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh);
if (geom->need_build_bvh(params.bvh_layout)) {
prim_index_size += bvh->pack.prim_index.size();
prim_tri_verts_size += bvh->pack.prim_tri_verts.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.prim_tri_verts.resize(prim_tri_verts_size);
pack.prim_tri_index.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) {
pack.prim_time.resize(prim_index_size);
}
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL;
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL;
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL;
float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL;
unordered_map<Geometry *, int> geometry_map;
/* merge */
foreach (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 */
unordered_map<Geometry *, int>::iterator it = geometry_map.find(geom);
if (geometry_map.find(geom) != geometry_map.end()) {
int noffset = it->second;
pack.object_node[object_offset++] = noffset;
continue;
}
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh);
int noffset = nodes_offset;
int noffset_leaf = nodes_leaf_offset;
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()) {
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
int *bvh_prim_index = &bvh->pack.prim_index[0];
int *bvh_prim_type = &bvh->pack.prim_type[0];
uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
for (size_t i = 0; i < bvh_prim_index_size; i++) {
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_prim_index_offset] = -1;
}
else {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
pack_prim_tri_verts_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 != NULL) {
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
}
pack_prim_index_offset++;
}
}
/* Merge triangle vertices data. */
if (bvh->pack.prim_tri_verts.size()) {
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
&bvh->pack.prim_tri_verts[0],
prim_tri_size * sizeof(float4));
pack_prim_tri_verts_offset += prim_tri_size;
}
/* merge nodes */
if (bvh->pack.leaf_nodes.size()) {
int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) {
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[0];
size_t bvh_nodes_size = bvh->pack.nodes.size();
for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
size_t nsize, nsize_bbox;
if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
nsize = BVH_UNALIGNED_NODE_SIZE;
nsize_bbox = 0;
}
else {
nsize = BVH_NODE_SIZE;
nsize_bbox = 0;
}
memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
/* 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.
*/
memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1],
&bvh_nodes[i + nsize_bbox + 1],
sizeof(int4) * (nsize - (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

View File

@ -26,23 +26,30 @@
CCL_NAMESPACE_BEGIN
class BVHNode;
struct BVHStackEntry;
class BVHParams;
class BoundBox;
class LeafNode;
class Object;
class Progress;
#define BVH_NODE_SIZE 4
#define BVH_NODE_LEAF_SIZE 1
#define BVH_UNALIGNED_NODE_SIZE 7
/* Pack Utility */
struct BVHStackEntry {
const BVHNode *node;
int idx;
BVHStackEntry(const BVHNode *n = 0, int i = 0);
int encodeIdx() const;
};
/* BVH2
*
* Typical BVH with each node having two children.
*/
class BVH2 : public BVH {
public:
void build(Progress &progress, Stats *stats);
void refit(Progress &progress);
PackedBVH pack;
protected:
/* constructor */
friend class BVH;
@ -51,10 +58,10 @@ class BVH2 : public BVH {
const vector<Object *> &objects);
/* Building process. */
virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
virtual BVHNode *widen_children_nodes(const BVHNode *root);
/* pack */
void pack_nodes(const BVHNode *root) override;
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);
@ -84,8 +91,18 @@ class BVH2 : public BVH {
uint visibility1);
/* refit */
void refit_nodes() override;
void refit_nodes();
void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility);
/* Refit range of primitives. */
void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
/* triangles and strands */
void pack_primitives();
void pack_triangle(int idx, float4 storage[3]);
/* merge instance BVH's */
void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
};
CCL_NAMESPACE_END

View File

@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n)
return !progress->get_cancel();
}
static size_t count_primitives(Geometry *geom)
{
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
return mesh->num_triangles();
}
else if (geom->geometry_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
return hair->num_segments();
}
return 0;
}
BVHEmbree::BVHEmbree(const BVHParams &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_,
const Device *device)
const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_),
scene(NULL),
mem_used(0),
top_level(NULL),
rtc_device((RTCDevice)device->bvh_device()),
stats(NULL),
curve_subdivisions(params.curve_subdivisions),
build_quality(RTC_BUILD_QUALITY_REFIT),
dynamic_scene(true)
rtc_device(NULL),
build_quality(RTC_BUILD_QUALITY_REFIT)
{
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
pack.root_index = -1;
}
BVHEmbree::~BVHEmbree()
{
if (!params.top_level) {
destroy(scene);
}
}
void BVHEmbree::destroy(RTCScene scene)
{
if (scene) {
rtcReleaseScene(scene);
scene = NULL;
}
}
void BVHEmbree::delete_rtcScene()
{
if (scene) {
/* When this BVH is used as an instance in a top level BVH, don't delete now
* Let the top_level BVH know that it should delete it later. */
if (top_level) {
top_level->add_delayed_delete_scene(scene);
}
else {
rtcReleaseScene(scene);
if (delayed_delete_scenes.size()) {
foreach (RTCScene s, delayed_delete_scenes) {
rtcReleaseScene(s);
}
}
delayed_delete_scenes.clear();
}
scene = NULL;
}
}
void BVHEmbree::build(Progress &progress, Stats *stats_)
void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
{
rtc_device = rtc_device_;
assert(rtc_device);
stats = stats_;
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
progress.set_substatus("Building BVH");
@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
RTC_BUILD_QUALITY_MEDIUM);
rtcSetSceneBuildQuality(scene, build_quality);
/* Count triangles and curves first, reserve arrays once. */
size_t prim_count = 0;
foreach (Object *ob, objects) {
if (params.top_level) {
if (!ob->is_traceable()) {
continue;
}
if (!ob->get_geometry()->is_instanced()) {
prim_count += count_primitives(ob->get_geometry());
}
else {
++prim_count;
}
}
else {
prim_count += count_primitives(ob->get_geometry());
}
}
pack.prim_object.reserve(prim_count);
pack.prim_type.reserve(prim_count);
pack.prim_index.reserve(prim_count);
pack.prim_tri_index.reserve(prim_count);
int i = 0;
pack.object_node.clear();
foreach (Object *ob, objects) {
if (params.top_level) {
if (!ob->is_traceable()) {
@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
}
if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return;
}
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
rtcCommitScene(scene);
pack_primitives();
if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return;
}
progress.set_substatus("Packing geometry");
pack_nodes(NULL);
stats = NULL;
}
void BVHEmbree::copy_to_device(Progress & /*progress*/, DeviceScene *dscene)
{
dscene->data.bvh.scene = scene;
}
BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/)
{
assert(!"Must not be called.");
return NULL;
}
void BVHEmbree::add_object(Object *ob, int i)
@ -498,15 +393,8 @@ void BVHEmbree::add_object(Object *ob, int i)
void BVHEmbree::add_instance(Object *ob, int i)
{
if (!ob || !ob->get_geometry()) {
assert(0);
return;
}
BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh);
if (instance_bvh->top_level != this) {
instance_bvh->top_level = this;
}
assert(instance_bvh != NULL);
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, RTC_MAX_TIME_STEP_COUNT);
@ -538,11 +426,6 @@ void BVHEmbree::add_instance(Object *ob, int i)
geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm());
}
pack.prim_index.push_back_slow(-1);
pack.prim_object.push_back_slow(i);
pack.prim_type.push_back_slow(PRIMITIVE_NONE);
pack.prim_tri_index.push_back_slow(-1);
rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@ -553,20 +436,22 @@ void BVHEmbree::add_instance(Object *ob, int i)
void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
{
size_t prim_offset = pack.prim_index.size();
size_t prim_offset = mesh->optix_prim_offset;
const Attribute *attr_mP = NULL;
size_t num_geometry_motion_steps = 1;
size_t num_motion_steps = 1;
if (mesh->has_motion_blur()) {
attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
num_geometry_motion_steps = mesh->get_motion_steps();
num_motion_steps = mesh->get_motion_steps();
}
}
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
num_motion_steps = min(num_motion_steps, 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);
@ -588,22 +473,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
set_tri_vertex_buffer(geom_id, mesh, false);
size_t prim_object_size = pack.prim_object.size();
pack.prim_object.resize(prim_object_size + num_triangles);
size_t prim_type_size = pack.prim_type.size();
pack.prim_type.resize(prim_type_size + num_triangles);
size_t prim_index_size = pack.prim_index.size();
pack.prim_index.resize(prim_index_size + num_triangles);
pack.prim_tri_index.resize(prim_index_size + num_triangles);
int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE);
for (size_t j = 0; j < num_triangles; ++j) {
pack.prim_object[prim_object_size + j] = i;
pack.prim_type[prim_type_size + j] = prim_type;
pack.prim_index[prim_index_size + j] = j;
pack.prim_tri_index[prim_index_size + j] = j;
}
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@ -629,12 +498,12 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con
}
}
}
const size_t num_verts = mesh->verts.size();
const size_t num_verts = mesh->get_verts().size();
for (int t = 0; t < num_motion_steps; ++t) {
const float3 *verts;
if (t == t_mid) {
verts = &mesh->verts[0];
verts = mesh->get_verts().data();
}
else {
int t_ = (t > t_mid) ? (t - 1) : t;
@ -736,24 +605,19 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
{
size_t prim_offset = pack.prim_index.size();
size_t prim_offset = hair->optix_prim_offset;
const Attribute *attr_mP = NULL;
size_t num_geometry_motion_steps = 1;
size_t num_motion_steps = 1;
if (hair->has_motion_blur()) {
attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
num_geometry_motion_steps = hair->get_motion_steps();
num_motion_steps = hair->get_motion_steps();
}
}
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const PrimitiveType primitive_type =
(num_motion_steps > 1) ?
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
PRIMITIVE_MOTION_CURVE_THICK) :
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const size_t num_curves = hair->num_curves();
size_t num_segments = 0;
@ -763,22 +627,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
num_segments += c.num_segments();
}
/* Make room for Cycles specific data. */
size_t prim_object_size = pack.prim_object.size();
pack.prim_object.resize(prim_object_size + num_segments);
size_t prim_type_size = pack.prim_type.size();
pack.prim_type.resize(prim_type_size + num_segments);
size_t prim_index_size = pack.prim_index.size();
pack.prim_index.resize(prim_index_size + num_segments);
size_t prim_tri_index_size = pack.prim_index.size();
pack.prim_tri_index.resize(prim_tri_index_size + num_segments);
enum RTCGeometryType type = (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, curve_subdivisions + 1);
rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1);
unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments);
size_t rtc_index = 0;
@ -788,11 +642,6 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtc_indices[rtc_index] = c.first_key + k;
/* Room for extra CVs at Catmull-Rom splines. */
rtc_indices[rtc_index] += j * 2;
/* Cycles specific data. */
pack.prim_object[prim_object_size + rtc_index] = i;
pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k));
pack.prim_index[prim_index_size + rtc_index] = j;
pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index;
++rtc_index;
}
@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtcReleaseGeometry(geom_id);
}
void BVHEmbree::pack_nodes(const BVHNode *)
void BVHEmbree::refit(Progress &progress)
{
/* Quite a bit of this code is for compatibility with Cycles' native BVH. */
if (!params.top_level) {
return;
}
progress.set_substatus("Refitting BVH nodes");
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;
}
}
size_t prim_offset = pack.prim_index.size();
/* reserve */
size_t prim_index_size = pack.prim_index.size();
size_t prim_tri_verts_size = pack.prim_tri_verts.size();
size_t pack_prim_index_offset = prim_index_size;
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
size_t object_offset = 0;
map<Geometry *, int> geometry_map;
foreach (Object *ob, objects) {
Geometry *geom = ob->get_geometry();
BVH *bvh = geom->bvh;
if (geom->need_build_bvh(BVH_LAYOUT_EMBREE)) {
if (geometry_map.find(geom) == geometry_map.end()) {
prim_index_size += bvh->pack.prim_index.size();
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
geometry_map[geom] = 1;
}
}
}
geometry_map.clear();
pack.prim_index.resize(prim_index_size);
pack.prim_type.resize(prim_index_size);
pack.prim_object.resize(prim_index_size);
pack.prim_visibility.clear();
pack.prim_tri_verts.resize(prim_tri_verts_size);
pack.prim_tri_index.resize(prim_index_size);
pack.object_node.resize(objects.size());
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
/* merge */
foreach (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(BVH_LAYOUT_EMBREE)) {
pack.object_node[object_offset++] = prim_offset;
continue;
}
/* if geom already added once, don't add it again, but used set
* node offset for this object */
map<Geometry *, int>::iterator it = geometry_map.find(geom);
if (geometry_map.find(geom) != geometry_map.end()) {
int noffset = it->second;
pack.object_node[object_offset++] = noffset;
continue;
}
BVHEmbree *bvh = (BVHEmbree *)geom->bvh;
rtc_memory_monitor_func(stats, unaccounted_mem, true);
unaccounted_mem = 0;
int geom_prim_offset = geom->prim_offset;
/* fill in node indexes for instances */
pack.object_node[object_offset++] = prim_offset;
geometry_map[geom] = pack.object_node[object_offset - 1];
/* merge primitive, object and triangle indexes */
if (bvh->pack.prim_index.size()) {
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
int *bvh_prim_index = &bvh->pack.prim_index[0];
int *bvh_prim_type = &bvh->pack.prim_type[0];
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
for (size_t i = 0; i < bvh_prim_index_size; ++i) {
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_prim_index_offset] = -1;
}
else {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
pack_prim_tri_verts_offset;
}
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
pack_prim_object[pack_prim_index_offset] = 0;
++pack_prim_index_offset;
}
}
/* Merge triangle vertices data. */
if (bvh->pack.prim_tri_verts.size()) {
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
&bvh->pack.prim_tri_verts[0],
prim_tri_size * sizeof(float4));
pack_prim_tri_verts_offset += prim_tri_size;
}
prim_offset += bvh->pack.prim_index.size();
}
}
void BVHEmbree::refit_nodes()
{
/* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */
unsigned geom_id = 0;
foreach (Object *ob, objects) {
@ -971,8 +696,10 @@ void BVHEmbree::refit_nodes()
}
geom_id += 2;
}
rtcCommitScene(scene);
}
CCL_NAMESPACE_END
#endif /* WITH_EMBREE */

View File

@ -31,56 +31,34 @@
CCL_NAMESPACE_BEGIN
class Geometry;
class Hair;
class Mesh;
class BVHEmbree : public BVH {
public:
virtual void build(Progress &progress, Stats *stats) override;
virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
virtual ~BVHEmbree();
RTCScene scene;
static void destroy(RTCScene);
void build(Progress &progress, Stats *stats, RTCDevice rtc_device);
void refit(Progress &progress);
/* Building process. */
virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
RTCScene scene;
protected:
friend class BVH;
BVHEmbree(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects,
const Device *device);
virtual void pack_nodes(const BVHNode *) override;
virtual void refit_nodes() override;
const vector<Object *> &objects);
virtual ~BVHEmbree();
void add_object(Object *ob, int i);
void add_instance(Object *ob, int i);
void add_curves(const Object *ob, const Hair *hair, int i);
void add_triangles(const Object *ob, const Mesh *mesh, int i);
ssize_t mem_used;
void add_delayed_delete_scene(RTCScene scene)
{
delayed_delete_scenes.push_back(scene);
}
BVHEmbree *top_level;
private:
void delete_rtcScene();
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);
RTCDevice rtc_device;
Stats *stats;
vector<RTCScene> delayed_delete_scenes;
int curve_subdivisions;
enum RTCBuildQuality build_quality;
bool dynamic_scene;
};
CCL_NAMESPACE_END

View File

@ -0,0 +1,37 @@
/*
* Copyright 2020, Blender Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bvh/bvh_multi.h"
#include "util/util_foreach.h"
CCL_NAMESPACE_BEGIN
BVHMulti::BVHMulti(const BVHParams &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_)
{
}
BVHMulti::~BVHMulti()
{
foreach (BVH *bvh, sub_bvhs) {
delete bvh;
}
}
CCL_NAMESPACE_END

View File

@ -0,0 +1,39 @@
/*
* Copyright 2020, Blender Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __BVH_MULTI_H__
#define __BVH_MULTI_H__
#include "bvh/bvh.h"
#include "bvh/bvh_params.h"
CCL_NAMESPACE_BEGIN
class BVHMulti : public BVH {
public:
vector<BVH *> sub_bvhs;
protected:
friend class BVH;
BVHMulti(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects);
virtual ~BVHMulti();
};
CCL_NAMESPACE_END
#endif /* __BVH_MULTI_H__ */

View File

@ -19,212 +19,22 @@
# include "bvh/bvh_optix.h"
# include "device/device.h"
# include "render/geometry.h"
# include "render/hair.h"
# include "render/mesh.h"
# include "render/object.h"
# include "util/util_foreach.h"
# include "util/util_logging.h"
# include "util/util_progress.h"
CCL_NAMESPACE_BEGIN
BVHOptiX::BVHOptiX(const BVHParams &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_)
const vector<Object *> &objects_,
Device *device)
: BVH(params_, geometry_, objects_),
traversable_handle(0),
as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
motion_transform_data(device, "optix motion transform")
{
optix_handle = 0;
optix_data_handle = 0;
do_refit = false;
}
BVHOptiX::~BVHOptiX()
{
}
void BVHOptiX::build(Progress &, Stats *)
{
if (params.top_level)
pack_tlas();
else
pack_blas();
}
void BVHOptiX::copy_to_device(Progress &progress, DeviceScene *dscene)
{
progress.set_status("Updating Scene BVH", "Building OptiX acceleration structure");
Device *const device = dscene->bvh_nodes.device;
if (!device->build_optix_bvh(this))
progress.set_error("Failed to build OptiX acceleration structure");
}
void BVHOptiX::pack_blas()
{
// Bottom-level BVH can contain multiple primitive types, so merge them:
assert(geometry.size() == 1 && objects.size() == 1); // These are built per-mesh
Geometry *const geom = geometry[0];
if (geom->geometry_type == Geometry::HAIR) {
Hair *const hair = static_cast<Hair *const>(geom);
if (hair->num_curves() > 0) {
const size_t num_curves = hair->num_curves();
const size_t num_segments = hair->num_segments();
pack.prim_type.reserve(pack.prim_type.size() + num_segments);
pack.prim_index.reserve(pack.prim_index.size() + num_segments);
pack.prim_object.reserve(pack.prim_object.size() + num_segments);
// 'pack.prim_time' is only used in geom_curve_intersect.h
// It is not needed because of OPTIX_MOTION_FLAG_[START|END]_VANISH
uint type = (hair->get_use_motion_blur() &&
hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) ?
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
PRIMITIVE_MOTION_CURVE_THICK) :
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON :
PRIMITIVE_CURVE_THICK);
for (size_t j = 0; j < num_curves; ++j) {
const Hair::Curve curve = hair->get_curve(j);
for (size_t k = 0; k < curve.num_segments(); ++k) {
pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
// Each curve segment points back to its curve index
pack.prim_index.push_back_reserved(j);
pack.prim_object.push_back_reserved(0);
}
}
}
}
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
Mesh *const mesh = static_cast<Mesh *const>(geom);
if (mesh->num_triangles() > 0) {
const size_t num_triangles = mesh->num_triangles();
pack.prim_type.reserve(pack.prim_type.size() + num_triangles);
pack.prim_index.reserve(pack.prim_index.size() + num_triangles);
pack.prim_object.reserve(pack.prim_object.size() + num_triangles);
uint type = PRIMITIVE_TRIANGLE;
if (mesh->get_use_motion_blur() && mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION))
type = PRIMITIVE_MOTION_TRIANGLE;
for (size_t k = 0; k < num_triangles; ++k) {
pack.prim_type.push_back_reserved(type);
pack.prim_index.push_back_reserved(k);
pack.prim_object.push_back_reserved(0);
}
}
}
// Initialize visibility to zero and later update it during top-level build
uint prev_visibility = objects[0]->get_visibility();
objects[0]->set_visibility(0);
// Update 'pack.prim_tri_index', 'pack.prim_tri_verts' and 'pack.prim_visibility'
pack_primitives();
// Reset visibility after packing
objects[0]->set_visibility(prev_visibility);
}
void BVHOptiX::pack_tlas()
{
// Calculate total packed size
size_t prim_index_size = 0;
size_t prim_tri_verts_size = 0;
foreach (Geometry *geom, geometry) {
BVH *const bvh = geom->bvh;
prim_index_size += bvh->pack.prim_index.size();
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
}
if (prim_index_size == 0)
return; // Abort right away if this is an empty BVH
size_t pack_offset = 0;
size_t pack_verts_offset = 0;
pack.prim_type.resize(prim_index_size);
int *pack_prim_type = pack.prim_type.data();
pack.prim_index.resize(prim_index_size);
int *pack_prim_index = pack.prim_index.data();
pack.prim_object.resize(prim_index_size);
int *pack_prim_object = pack.prim_object.data();
pack.prim_visibility.resize(prim_index_size);
uint *pack_prim_visibility = pack.prim_visibility.data();
pack.prim_tri_index.resize(prim_index_size);
uint *pack_prim_tri_index = pack.prim_tri_index.data();
pack.prim_tri_verts.resize(prim_tri_verts_size);
float4 *pack_prim_tri_verts = pack.prim_tri_verts.data();
// Top-level BVH should only contain instances, see 'Geometry::need_build_bvh'
// Iterate over scene mesh list instead of objects, since the 'prim_offset' is calculated based
// on that list, which may be ordered differently from the object list.
foreach (Geometry *geom, geometry) {
PackedBVH &bvh_pack = geom->bvh->pack;
int geom_prim_offset = geom->prim_offset;
// Merge visibility flags of all objects and fix object indices for non-instanced geometry
int object_index = 0; // Unused for instanced geometry
int object_visibility = 0;
foreach (Object *ob, objects) {
if (ob->get_geometry() == geom) {
object_visibility |= ob->visibility_for_tracing();
if (!geom->is_instanced()) {
object_index = ob->get_device_index();
break;
}
}
}
// Merge primitive, object and triangle indexes
if (!bvh_pack.prim_index.empty()) {
int *bvh_prim_type = &bvh_pack.prim_type[0];
int *bvh_prim_index = &bvh_pack.prim_index[0];
uint *bvh_prim_tri_index = &bvh_pack.prim_tri_index[0];
uint *bvh_prim_visibility = &bvh_pack.prim_visibility[0];
for (size_t i = 0; i < bvh_pack.prim_index.size(); i++, pack_offset++) {
if (bvh_pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_offset] = -1;
}
else {
pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_tri_index[pack_offset] = bvh_prim_tri_index[i] + pack_verts_offset;
}
pack_prim_type[pack_offset] = bvh_prim_type[i];
pack_prim_object[pack_offset] = object_index;
pack_prim_visibility[pack_offset] = bvh_prim_visibility[i] | object_visibility;
}
}
// Merge triangle vertex data
if (!bvh_pack.prim_tri_verts.empty()) {
const size_t prim_tri_size = bvh_pack.prim_tri_verts.size();
memcpy(pack_prim_tri_verts + pack_verts_offset,
bvh_pack.prim_tri_verts.data(),
prim_tri_size * sizeof(float4));
pack_verts_offset += prim_tri_size;
}
}
}
void BVHOptiX::pack_nodes(const BVHNode *)
{
}
void BVHOptiX::refit_nodes()
{
do_refit = true;
}
BVHNode *BVHOptiX::widen_children_nodes(const BVHNode *)
{
return NULL;
// Acceleration structure memory is freed via the 'as_data' destructor
}
CCL_NAMESPACE_END

View File

@ -26,33 +26,19 @@
CCL_NAMESPACE_BEGIN
class Geometry;
class Optix;
class BVHOptiX : public BVH {
friend class BVH;
public:
uint64_t optix_handle;
uint64_t optix_data_handle;
bool do_refit;
uint64_t traversable_handle;
device_only_memory<char> as_data;
device_only_memory<char> motion_transform_data;
protected:
friend class BVH;
BVHOptiX(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects);
const vector<Object *> &objects,
Device *device);
virtual ~BVHOptiX();
virtual void build(Progress &progress, Stats *) override;
virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
private:
void pack_blas();
void pack_tlas();
virtual void pack_nodes(const BVHNode *) override;
virtual void refit_nodes() override;
virtual BVHNode *widen_children_nodes(const BVHNode *) override;
};
CCL_NAMESPACE_END

View File

@ -71,6 +71,7 @@ class CUDADevice : public Device {
};
typedef map<device_memory *, CUDAMem> CUDAMemMap;
CUDAMemMap cuda_mem_map;
thread_mutex cuda_mem_map_mutex;
struct PixelMem {
GLuint cuPBO;

View File

@ -718,8 +718,10 @@ void CUDADevice::init_host_memory()
void CUDADevice::load_texture_info()
{
if (need_texture_info) {
texture_info.copy_to_device();
/* Unset flag before copying, so this does not loop indefinitely if the copy below calls
* into 'move_textures_to_host' (which calls 'load_texture_info' again). */
need_texture_info = false;
texture_info.copy_to_device();
}
}
@ -988,6 +990,7 @@ void CUDADevice::mem_alloc(device_memory &mem)
assert(!"mem_alloc not supported for global memory.");
}
else {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem);
}
}
@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem)
tex_alloc((device_texture &)mem);
}
else {
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.device_pointer) {
generic_alloc(mem);
}
generic_copy_to(mem);
}
}
@ -1048,6 +1051,7 @@ void CUDADevice::mem_zero(device_memory &mem)
/* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory
* regardless of mem.host_pointer and mem.shared_pointer. */
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
const CUDAContextScope scope(this);
cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size()));
@ -1069,6 +1073,7 @@ void CUDADevice::mem_free(device_memory &mem)
tex_free((device_texture &)mem);
}
else {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem);
}
}
@ -1092,6 +1097,7 @@ void CUDADevice::const_copy_to(const char *name, void *host, size_t size)
void CUDADevice::global_alloc(device_memory &mem)
{
if (mem.is_resident(this)) {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem);
generic_copy_to(mem);
}
@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem)
void CUDADevice::global_free(device_memory &mem)
{
if (mem.is_resident(this) && mem.device_pointer) {
thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem);
}
}
@ -1170,6 +1177,8 @@ void CUDADevice::tex_alloc(device_texture &mem)
size_t src_pitch = mem.data_width * dsize * mem.data_elements;
size_t dst_pitch = src_pitch;
thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.is_resident(this)) {
cmem = &cuda_mem_map[&mem];
cmem->texobject = 0;
@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size));
}
/* Unlock mutex before resizing texture info, since that may attempt to lock it again. */
lock.unlock();
/* Resize once */
const uint slot = mem.slot;
if (slot >= texture_info.size()) {
@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem)
texDesc.filterMode = filter_mode;
texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES;
/* Lock again and refresh the data pointer (in case another thread modified the map in the
* meantime). */
lock.lock();
cmem = &cuda_mem_map[&mem];
cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
texture_info[slot].data = (uint64_t)cmem->texobject;
@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem)
{
if (mem.device_pointer) {
CUDAContextScope scope(this);
thread_scoped_lock lock(cuda_mem_map_mutex);
const CUDAMem &cmem = cuda_mem_map[&mem];
if (cmem.texobject) {

View File

@ -17,6 +17,8 @@
#include <stdlib.h>
#include <string.h>
#include "bvh/bvh2.h"
#include "device/device.h"
#include "device/device_intern.h"
@ -364,6 +366,19 @@ void Device::draw_pixels(device_memory &rgba,
}
}
void Device::build_bvh(BVH *bvh, Progress &progress, bool refit)
{
assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2);
BVH2 *const bvh2 = static_cast<BVH2 *>(bvh);
if (refit) {
bvh2->refit(progress);
}
else {
bvh2->build(progress, &stats);
}
}
Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
{
#ifdef WITH_MULTI

View File

@ -373,12 +373,6 @@ class Device {
return NULL;
}
/* Device specific pointer for BVH creation. Currently only used by Embree. */
virtual void *bvh_device() const
{
return NULL;
}
/* load/compile kernels, must be called before adding tasks */
virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/)
{
@ -427,10 +421,7 @@ class Device {
const DeviceDrawParams &draw_params);
/* acceleration structure building */
virtual bool build_optix_bvh(BVH *)
{
return false;
}
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
#ifdef WITH_NETWORK
/* networking */

View File

@ -47,6 +47,8 @@
#include "kernel/osl/osl_globals.h"
// clang-format on
#include "bvh/bvh_embree.h"
#include "render/buffers.h"
#include "render/coverage.h"
@ -188,6 +190,7 @@ class CPUDevice : public Device {
#endif
thread_spin_lock oidn_task_lock;
#ifdef WITH_EMBREE
RTCScene embree_scene = NULL;
RTCDevice embree_device;
#endif
@ -472,6 +475,15 @@ class CPUDevice : public Device {
virtual void const_copy_to(const char *name, void *host, size_t size) override
{
#if WITH_EMBREE
if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData));
// Update scene handle (since it is different for each device on multi devices)
KernelData *const data = (KernelData *)host;
data->bvh.scene = embree_scene;
}
#endif
kernel_const_copy(&kernel_globals, name, host, size);
}
@ -537,13 +549,26 @@ class CPUDevice : public Device {
#endif
}
void *bvh_device() const override
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
#ifdef WITH_EMBREE
return embree_device;
#else
return NULL;
if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE ||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) {
BVHEmbree *const bvh_embree = static_cast<BVHEmbree *>(bvh);
if (refit) {
bvh_embree->refit(progress);
}
else {
bvh_embree->build(progress, &stats, embree_device);
}
if (bvh->params.top_level) {
embree_scene = bvh_embree->scene;
}
}
else
#endif
Device::build_bvh(bvh, progress, refit);
}
void thread_run(DeviceTask &task)

View File

@ -17,11 +17,14 @@
#include <sstream>
#include <stdlib.h>
#include "bvh/bvh_multi.h"
#include "device/device.h"
#include "device/device_intern.h"
#include "device/device_network.h"
#include "render/buffers.h"
#include "render/geometry.h"
#include "util/util_foreach.h"
#include "util/util_list.h"
@ -141,7 +144,7 @@ class MultiDevice : public Device {
delete sub.device;
}
const string &error_message()
const string &error_message() override
{
error_msg.clear();
@ -153,7 +156,7 @@ class MultiDevice : public Device {
return error_msg;
}
virtual bool show_samples() const
virtual bool show_samples() const override
{
if (devices.size() > 1) {
return false;
@ -161,16 +164,31 @@ class MultiDevice : public Device {
return devices.front().device->show_samples();
}
virtual BVHLayoutMask get_bvh_layout_mask() const
virtual BVHLayoutMask get_bvh_layout_mask() const override
{
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL;
BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE;
foreach (const SubDevice &sub_device, devices) {
bvh_layout_mask &= sub_device.device->get_bvh_layout_mask();
BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask();
bvh_layout_mask &= device_bvh_layout_mask;
bvh_layout_mask_all |= device_bvh_layout_mask;
}
/* With multiple OptiX devices, every device needs its own acceleration structure */
if (bvh_layout_mask == BVH_LAYOUT_OPTIX) {
return BVH_LAYOUT_MULTI_OPTIX;
}
/* When devices do not share a common BVH layout, fall back to creating one for each */
const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE);
if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) {
return BVH_LAYOUT_MULTI_OPTIX_EMBREE;
}
return bvh_layout_mask;
}
bool load_kernels(const DeviceRequestedFeatures &requested_features)
bool load_kernels(const DeviceRequestedFeatures &requested_features) override
{
foreach (SubDevice &sub, devices)
if (!sub.device->load_kernels(requested_features))
@ -188,7 +206,7 @@ class MultiDevice : public Device {
return true;
}
bool wait_for_availability(const DeviceRequestedFeatures &requested_features)
bool wait_for_availability(const DeviceRequestedFeatures &requested_features) override
{
foreach (SubDevice &sub, devices)
if (!sub.device->wait_for_availability(requested_features))
@ -203,7 +221,7 @@ class MultiDevice : public Device {
return true;
}
DeviceKernelStatus get_active_kernel_switch_state()
DeviceKernelStatus get_active_kernel_switch_state() override
{
DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL;
@ -227,24 +245,61 @@ class MultiDevice : public Device {
return result;
}
bool build_optix_bvh(BVH *bvh)
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
/* Broadcast acceleration structure build to all render devices */
foreach (SubDevice &sub, devices) {
if (!sub.device->build_optix_bvh(bvh))
return false;
/* Try to build and share a single acceleration structure, if possible */
if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) {
devices.back().device->build_bvh(bvh, progress, refit);
return;
}
BVHMulti *const bvh_multi = static_cast<BVHMulti *>(bvh);
bvh_multi->sub_bvhs.resize(devices.size());
vector<BVHMulti *> geom_bvhs;
geom_bvhs.reserve(bvh->geometry.size());
foreach (Geometry *geom, bvh->geometry) {
geom_bvhs.push_back(static_cast<BVHMulti *>(geom->bvh));
}
/* Broadcast acceleration structure build to all render devices */
size_t i = 0;
foreach (SubDevice &sub, devices) {
/* Change geometry BVH pointers to the sub BVH */
for (size_t k = 0; k < bvh->geometry.size(); ++k) {
bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i];
}
if (!bvh_multi->sub_bvhs[i]) {
BVHParams params = bvh->params;
if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX)
params.bvh_layout = BVH_LAYOUT_OPTIX;
else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE)
params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX :
BVH_LAYOUT_EMBREE;
/* Skip building a bottom level acceleration structure for non-instanced geometry on Embree
* (since they are put into the top level directly, see bvh_embree.cpp) */
if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE &&
!bvh->geometry[0]->is_instanced()) {
i++;
continue;
}
bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device);
}
sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit);
i++;
}
/* Change geomtry BVH pointers back to the multi BVH */
for (size_t k = 0; k < bvh->geometry.size(); ++k) {
bvh->geometry[k]->bvh = geom_bvhs[k];
}
return true;
}
virtual void *bvh_device() const
{
/* CPU devices will always be at the back, so simply choose the last one.
There should only ever be one CPU device anyway and we need the Embree device for it. */
return devices.back().device->bvh_device();
}
virtual void *osl_memory()
virtual void *osl_memory() override
{
if (devices.size() > 1) {
return NULL;
@ -252,7 +307,7 @@ class MultiDevice : public Device {
return devices.front().device->osl_memory();
}
bool is_resident(device_ptr key, Device *sub_device)
bool is_resident(device_ptr key, Device *sub_device) override
{
foreach (SubDevice &sub, devices) {
if (sub.device == sub_device) {
@ -299,7 +354,7 @@ class MultiDevice : public Device {
return find_matching_mem_device(key, sub)->ptr_map[key];
}
void mem_alloc(device_memory &mem)
void mem_alloc(device_memory &mem) override
{
device_ptr key = unique_key++;
@ -335,7 +390,7 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size);
}
void mem_copy_to(device_memory &mem)
void mem_copy_to(device_memory &mem) override
{
device_ptr existing_key = mem.device_pointer;
device_ptr key = (existing_key) ? existing_key : unique_key++;
@ -378,7 +433,7 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size - existing_size);
}
void mem_copy_from(device_memory &mem, int y, int w, int h, int elem)
void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override
{
device_ptr key = mem.device_pointer;
int i = 0, sub_h = h / devices.size();
@ -399,7 +454,7 @@ class MultiDevice : public Device {
mem.device_pointer = key;
}
void mem_zero(device_memory &mem)
void mem_zero(device_memory &mem) override
{
device_ptr existing_key = mem.device_pointer;
device_ptr key = (existing_key) ? existing_key : unique_key++;
@ -454,7 +509,7 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size - existing_size);
}
void mem_free(device_memory &mem)
void mem_free(device_memory &mem) override
{
device_ptr key = mem.device_pointer;
size_t existing_size = mem.device_size;
@ -510,7 +565,7 @@ class MultiDevice : public Device {
stats.mem_free(existing_size);
}
void const_copy_to(const char *name, void *host, size_t size)
void const_copy_to(const char *name, void *host, size_t size) override
{
foreach (SubDevice &sub, devices)
sub.device->const_copy_to(name, host, size);
@ -527,7 +582,7 @@ class MultiDevice : public Device {
int dw,
int dh,
bool transparent,
const DeviceDrawParams &draw_params)
const DeviceDrawParams &draw_params) override
{
assert(rgba.type == MEM_PIXELS);
@ -551,7 +606,7 @@ class MultiDevice : public Device {
rgba.device_pointer = key;
}
void map_tile(Device *sub_device, RenderTile &tile)
void map_tile(Device *sub_device, RenderTile &tile) override
{
if (!tile.buffer) {
return;
@ -572,7 +627,7 @@ class MultiDevice : public Device {
}
}
int device_number(Device *sub_device)
int device_number(Device *sub_device) override
{
int i = 0;
@ -591,7 +646,7 @@ class MultiDevice : public Device {
return -1;
}
void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override
{
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
RenderTile &tile = neighbors.tiles[i];
@ -643,7 +698,7 @@ class MultiDevice : public Device {
}
}
void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override
{
RenderTile &target_tile = neighbors.target;
device_vector<float> &mem = target_tile.buffers->buffer;
@ -677,7 +732,7 @@ class MultiDevice : public Device {
}
}
int get_split_task_count(DeviceTask &task)
int get_split_task_count(DeviceTask &task) override
{
int total_tasks = 0;
list<DeviceTask> tasks;
@ -693,7 +748,7 @@ class MultiDevice : public Device {
return total_tasks;
}
void task_add(DeviceTask &task)
void task_add(DeviceTask &task) override
{
list<SubDevice> task_devices = devices;
if (!denoising_devices.empty()) {
@ -743,7 +798,7 @@ class MultiDevice : public Device {
}
}
void task_wait()
void task_wait() override
{
foreach (SubDevice &sub, devices)
sub.device->task_wait();
@ -751,7 +806,7 @@ class MultiDevice : public Device {
sub.device->task_wait();
}
void task_cancel()
void task_cancel() override
{
foreach (SubDevice &sub, devices)
sub.device->task_cancel();

View File

@ -31,6 +31,7 @@
# include "util/util_logging.h"
# include "util/util_md5.h"
# include "util/util_path.h"
# include "util/util_progress.h"
# include "util/util_time.h"
# ifdef WITH_CUDA_DYNLOAD
@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice {
bool motion_blur = false;
device_vector<SbtRecord> sbt_data;
device_only_memory<KernelParams> launch_params;
vector<CUdeviceptr> as_mem;
OptixTraversableHandle tlas_handle = 0;
OptixDenoiser denoiser = NULL;
@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice {
// Make CUDA context current
const CUDAContextScope scope(cuContext);
// Free all acceleration structures
for (CUdeviceptr mem : as_mem) {
cuMemFree(mem);
}
sbt_data.free();
texture_info.free();
launch_params.free();
@ -297,6 +292,10 @@ class OptiXDevice : public CUDADevice {
BVHLayoutMask get_bvh_layout_mask() const override
{
// CUDA kernels are used when doing baking, so need to build a BVH those can understand too!
if (optix_module == NULL)
return CUDADevice::get_bvh_layout_mask();
// OptiX has its own internal acceleration structure format
return BVH_LAYOUT_OPTIX;
}
@ -330,10 +329,9 @@ class OptiXDevice : public CUDADevice {
return false;
}
// Disable baking for now, since its kernel is not well-suited for inlining and is very slow
// Baking is currently performed using CUDA, so no need to load OptiX kernels
if (requested_features.use_baking) {
set_error("OptiX backend does not support baking yet");
return false;
return true;
}
const CUDAContextScope scope(cuContext);
@ -700,6 +698,11 @@ class OptiXDevice : public CUDADevice {
while (task.acquire_tile(this, tile, task.tile_types)) {
if (tile.task == RenderTile::PATH_TRACE)
launch_render(task, tile, thread_index);
else if (tile.task == RenderTile::BAKE) {
// Perform baking using CUDA, since it is not currently implemented in OptiX
device_vector<WorkTile> work_tiles(this, "work_tiles", MEM_READ_ONLY);
CUDADevice::render(task, tile, work_tiles);
}
else if (tile.task == RenderTile::DENOISE)
launch_denoise(task, tile);
task.release_tile(tile);
@ -1128,11 +1131,10 @@ class OptiXDevice : public CUDADevice {
}
}
bool build_optix_bvh(const OptixBuildInput &build_input,
uint16_t num_motion_steps,
OptixTraversableHandle &out_handle,
CUdeviceptr &out_data,
OptixBuildOperation operation)
bool build_optix_bvh(BVHOptiX *bvh,
OptixBuildOperation operation,
const OptixBuildInput &build_input,
uint16_t num_motion_steps)
{
const CUDAContextScope scope(cuContext);
@ -1158,24 +1160,21 @@ class OptiXDevice : public CUDADevice {
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
// Allocate required output buffers
device_only_memory<char> temp_mem(this, "temp_build_mem");
device_only_memory<char> temp_mem(this, "optix temp as build mem");
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
if (!temp_mem.device_pointer)
return false; // Make sure temporary memory allocation succeeded
// Move textures to host memory if there is not enough room
size_t size = 0, free = 0;
cuMemGetInfo(&free, &size);
size = sizes.outputSizeInBytes + device_working_headroom;
if (size >= free && can_map_host) {
move_textures_to_host(size - free, false);
}
device_only_memory<char> &out_data = bvh->as_data;
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
check_result_cuda_ret(cuMemAlloc(&out_data, sizes.outputSizeInBytes));
assert(out_data.device == this);
out_data.alloc_to_device(sizes.outputSizeInBytes);
if (!out_data.device_pointer)
return false;
}
else {
assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes);
}
as_mem.push_back(out_data);
// Finally build the acceleration structure
OptixAccelEmitDesc compacted_size_prop;
@ -1184,6 +1183,7 @@ class OptiXDevice : public CUDADevice {
// Make sure this pointer is 8-byte aligned
compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8);
OptixTraversableHandle out_handle = 0;
check_result_optix_ret(optixAccelBuild(context,
NULL,
&options,
@ -1191,11 +1191,12 @@ class OptiXDevice : public CUDADevice {
1,
temp_mem.device_pointer,
sizes.tempSizeInBytes,
out_data,
out_data.device_pointer,
sizes.outputSizeInBytes,
&out_handle,
background ? &compacted_size_prop : NULL,
background ? 1 : 0));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for all operations to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
@ -1211,81 +1212,60 @@ class OptiXDevice : public CUDADevice {
// There is no point compacting if the size does not change
if (compacted_size < sizes.outputSizeInBytes) {
CUdeviceptr compacted_data = 0;
if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS)
device_only_memory<char> compacted_data(this, "optix compacted as");
compacted_data.alloc_to_device(compacted_size);
if (!compacted_data.device_pointer)
// Do not compact if memory allocation for compacted acceleration structure fails
// Can just use the uncompacted one then, so succeed here regardless
return true;
as_mem.push_back(compacted_data);
check_result_optix_ret(optixAccelCompact(
context, NULL, out_handle, compacted_data, compacted_size, &out_handle));
check_result_optix_ret(optixAccelCompact(context,
NULL,
out_handle,
compacted_data.device_pointer,
compacted_size,
&out_handle));
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for compaction to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
// Free uncompacted acceleration structure
cuMemFree(out_data);
as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array
std::swap(out_data.device_size, compacted_data.device_size);
std::swap(out_data.device_pointer, compacted_data.device_pointer);
}
}
return true;
}
bool build_optix_bvh(BVH *bvh) override
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
assert(bvh->params.top_level);
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
unsigned int num_instances = 0;
unordered_map<Geometry *, OptixTraversableHandle> geometry;
geometry.reserve(bvh->geometry.size());
progress.set_substatus("Building OptiX acceleration structure");
// Free all previous acceleration structures which can not be refit
std::set<CUdeviceptr> refit_mem;
if (!bvh->params.top_level) {
assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1);
for (Geometry *geom : bvh->geometry) {
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit) {
refit_mem.insert(static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle);
}
}
for (CUdeviceptr mem : as_mem) {
if (refit_mem.find(mem) == refit_mem.end()) {
cuMemFree(mem);
}
}
as_mem.clear();
// Build bottom level acceleration structures (BLAS)
// Note: Always keep this logic in sync with bvh_optix.cpp!
for (Object *ob : bvh->objects) {
// Skip geometry for which acceleration structure already exists
Geometry *geom = ob->get_geometry();
if (geometry.find(geom) != geometry.end())
continue;
OptixTraversableHandle handle;
OptixBuildOperation operation;
CUdeviceptr out_data;
// Refit is only possible in viewport for now.
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit && !background) {
out_data = static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle;
handle = static_cast<BVHOptiX *>(geom->bvh)->optix_handle;
// Refit is only possible in viewport for now (because AS is built with
// OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above)
OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD;
if (refit && !background) {
assert(bvh_optix->traversable_handle != 0);
operation = OPTIX_BUILD_OPERATION_UPDATE;
}
else {
out_data = 0;
handle = 0;
operation = OPTIX_BUILD_OPERATION_BUILD;
bvh_optix->as_data.free();
bvh_optix->traversable_handle = 0;
}
// Build bottom level acceleration structures (BLAS)
Geometry *const geom = bvh->geometry[0];
if (geom->geometry_type == Geometry::HAIR) {
// Build BLAS for curve primitives
Hair *const hair = static_cast<Hair *const>(ob->get_geometry());
Hair *const hair = static_cast<Hair *const>(geom);
if (hair->num_curves() == 0) {
continue;
return;
}
const size_t num_segments = hair->num_segments();
@ -1296,10 +1276,10 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = hair->get_motion_steps();
}
device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY);
device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY);
# if OPTIX_ABI_VERSION >= 36
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
device_vector<float4> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
// Four control points for each curve segment
const size_t num_vertices = num_segments * 4;
if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
@ -1317,7 +1297,7 @@ class OptiXDevice : public CUDADevice {
size_t center_step = (num_motion_steps - 1) / 2;
if (step != center_step) {
size_t attr_offset = (step > center_step) ? step - 1 : step;
// Technically this is a float4 array, but sizeof(float3) is the same as sizeof(float4)
// Technically this is a float4 array, but sizeof(float3) == sizeof(float4)
keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size();
}
@ -1444,22 +1424,15 @@ class OptiXDevice : public CUDADevice {
# endif
}
// Allocate memory for new BLAS and build it
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
geometry.insert({ob->get_geometry(), handle});
static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
}
else {
return false;
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
}
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
// Build BLAS for triangle primitives
Mesh *const mesh = static_cast<Mesh *const>(ob->get_geometry());
Mesh *const mesh = static_cast<Mesh *const>(geom);
if (mesh->num_triangles() == 0) {
continue;
return;
}
const size_t num_verts = mesh->get_verts().size();
@ -1470,12 +1443,12 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = mesh->get_motion_steps();
}
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
index_data.alloc(mesh->get_triangles().size());
memcpy(index_data.data(),
mesh->get_triangles().data(),
mesh->get_triangles().size() * sizeof(int));
device_vector<float3> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
device_vector<float3> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
vertex_data.alloc(num_verts * num_motion_steps);
for (size_t step = 0; step < num_motion_steps; ++step) {
@ -1520,190 +1493,208 @@ class OptiXDevice : public CUDADevice {
build_input.triangleArray.numSbtRecords = 1;
build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset;
// Allocate memory for new BLAS and build it
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
geometry.insert({ob->get_geometry(), handle});
static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
}
else {
return false;
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
}
}
else {
unsigned int num_instances = 0;
// Fill instance descriptions
bvh_optix->as_data.free();
bvh_optix->traversable_handle = 0;
bvh_optix->motion_transform_data.free();
// Fill instance descriptions
# if OPTIX_ABI_VERSION < 41
device_vector<OptixAabb> aabbs(this, "tlas_aabbs", MEM_READ_ONLY);
aabbs.alloc(bvh->objects.size());
device_vector<OptixAabb> aabbs(this, "optix tlas aabbs", MEM_READ_ONLY);
aabbs.alloc(bvh->objects.size());
# endif
device_vector<OptixInstance> instances(this, "tlas_instances", MEM_READ_ONLY);
instances.alloc(bvh->objects.size());
device_vector<OptixInstance> instances(this, "optix tlas instances", MEM_READ_ONLY);
instances.alloc(bvh->objects.size());
for (Object *ob : bvh->objects) {
// Skip non-traceable objects
if (!ob->is_traceable())
continue;
// Calculate total motion transform size and allocate memory for them
size_t motion_transform_offset = 0;
if (motion_blur) {
size_t total_motion_transform_size = 0;
for (Object *const ob : bvh->objects) {
if (ob->is_traceable() && ob->use_motion()) {
total_motion_transform_size = align_up(total_motion_transform_size,
OPTIX_TRANSFORM_BYTE_ALIGNMENT);
const size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
total_motion_transform_size = total_motion_transform_size +
sizeof(OptixSRTMotionTransform) +
motion_keys * sizeof(OptixSRTData);
}
}
// Create separate instance for triangle/curve meshes of an object
const auto handle_it = geometry.find(ob->get_geometry());
if (handle_it == geometry.end()) {
continue;
}
OptixTraversableHandle handle = handle_it->second;
# if OPTIX_ABI_VERSION < 41
OptixAabb &aabb = aabbs[num_instances];
aabb.minX = ob->bounds.min.x;
aabb.minY = ob->bounds.min.y;
aabb.minZ = ob->bounds.min.z;
aabb.maxX = ob->bounds.max.x;
aabb.maxY = ob->bounds.max.y;
aabb.maxZ = ob->bounds.max.z;
# endif
OptixInstance &instance = instances[num_instances++];
memset(&instance, 0, sizeof(instance));
// Clear transform to identity matrix
instance.transform[0] = 1.0f;
instance.transform[5] = 1.0f;
instance.transform[10] = 1.0f;
// Set user instance ID to object index
instance.instanceId = ob->get_device_index();
// Have to have at least one bit in the mask, or else instance would always be culled
instance.visibilityMask = 1;
if (ob->get_geometry()->has_volume) {
// Volumes have a special bit set in the visibility mask so a trace can mask only volumes
instance.visibilityMask |= 2;
assert(bvh_optix->motion_transform_data.device == this);
bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size);
}
if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
// Same applies to curves (so they can be skipped in local trace calls)
instance.visibilityMask |= 4;
for (Object *ob : bvh->objects) {
// Skip non-traceable objects
if (!ob->is_traceable())
continue;
BVHOptiX *const blas = static_cast<BVHOptiX *>(ob->get_geometry()->bvh);
OptixTraversableHandle handle = blas->traversable_handle;
# if OPTIX_ABI_VERSION < 41
OptixAabb &aabb = aabbs[num_instances];
aabb.minX = ob->bounds.min.x;
aabb.minY = ob->bounds.min.y;
aabb.minZ = ob->bounds.min.z;
aabb.maxX = ob->bounds.max.x;
aabb.maxY = ob->bounds.max.y;
aabb.maxZ = ob->bounds.max.z;
# endif
OptixInstance &instance = instances[num_instances++];
memset(&instance, 0, sizeof(instance));
// Clear transform to identity matrix
instance.transform[0] = 1.0f;
instance.transform[5] = 1.0f;
instance.transform[10] = 1.0f;
// Set user instance ID to object index
instance.instanceId = ob->get_device_index();
// Have to have at least one bit in the mask, or else instance would always be culled
instance.visibilityMask = 1;
if (ob->get_geometry()->has_volume) {
// Volumes have a special bit set in the visibility mask so a trace can mask only volumes
instance.visibilityMask |= 2;
}
if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
// Same applies to curves (so they can be skipped in local trace calls)
instance.visibilityMask |= 4;
# if OPTIX_ABI_VERSION >= 36
if (motion_blur && ob->get_geometry()->has_motion_blur() &&
DebugFlags().optix.curves_api &&
static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
// Select between motion blur and non-motion blur built-in intersection module
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
if (motion_blur && ob->get_geometry()->has_motion_blur() &&
DebugFlags().optix.curves_api &&
static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
// Select between motion blur and non-motion blur built-in intersection module
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
# endif
}
// Insert motion traversable if object has motion
if (motion_blur && ob->use_motion()) {
size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
motion_keys * sizeof(OptixSRTData);
const CUDAContextScope scope(cuContext);
CUdeviceptr motion_transform_gpu = 0;
check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size));
as_mem.push_back(motion_transform_gpu);
// Allocate host side memory for motion transform and fill it with transform data
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
new uint8_t[motion_transform_size]);
motion_transform.child = handle;
motion_transform.motionOptions.numKeys = ob->get_motion().size();
motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
motion_transform.motionOptions.timeBegin = 0.0f;
motion_transform.motionOptions.timeEnd = 1.0f;
OptixSRTData *const srt_data = motion_transform.srtData;
array<DecomposedTransform> decomp(ob->get_motion().size());
transform_motion_decompose(
decomp.data(), ob->get_motion().data(), ob->get_motion().size());
for (size_t i = 0; i < ob->get_motion().size(); ++i) {
// Scale
srt_data[i].sx = decomp[i].y.w; // scale.x.x
srt_data[i].sy = decomp[i].z.w; // scale.y.y
srt_data[i].sz = decomp[i].w.w; // scale.z.z
// Shear
srt_data[i].a = decomp[i].z.x; // scale.x.y
srt_data[i].b = decomp[i].z.y; // scale.x.z
srt_data[i].c = decomp[i].w.x; // scale.y.z
assert(decomp[i].z.z == 0.0f); // scale.y.x
assert(decomp[i].w.y == 0.0f); // scale.z.x
assert(decomp[i].w.z == 0.0f); // scale.z.y
// Pivot point
srt_data[i].pvx = 0.0f;
srt_data[i].pvy = 0.0f;
srt_data[i].pvz = 0.0f;
// Rotation
srt_data[i].qx = decomp[i].x.x;
srt_data[i].qy = decomp[i].x.y;
srt_data[i].qz = decomp[i].x.z;
srt_data[i].qw = decomp[i].x.w;
// Translation
srt_data[i].tx = decomp[i].y.x;
srt_data[i].ty = decomp[i].y.y;
srt_data[i].tz = decomp[i].y.z;
}
// Upload motion transform to GPU
cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
// Insert motion traversable if object has motion
if (motion_blur && ob->use_motion()) {
size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
motion_keys * sizeof(OptixSRTData);
// Disable instance transform if object uses motion transform already
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
const CUDAContextScope scope(cuContext);
// Get traversable handle to motion transform
optixConvertPointerToTraversableHandle(context,
motion_transform_gpu,
OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
&instance.traversableHandle);
}
else {
instance.traversableHandle = handle;
motion_transform_offset = align_up(motion_transform_offset,
OPTIX_TRANSFORM_BYTE_ALIGNMENT);
CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer +
motion_transform_offset;
motion_transform_offset += motion_transform_size;
if (ob->get_geometry()->is_instanced()) {
// Set transform matrix
memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
// Allocate host side memory for motion transform and fill it with transform data
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
new uint8_t[motion_transform_size]);
motion_transform.child = handle;
motion_transform.motionOptions.numKeys = ob->get_motion().size();
motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
motion_transform.motionOptions.timeBegin = 0.0f;
motion_transform.motionOptions.timeEnd = 1.0f;
OptixSRTData *const srt_data = motion_transform.srtData;
array<DecomposedTransform> decomp(ob->get_motion().size());
transform_motion_decompose(
decomp.data(), ob->get_motion().data(), ob->get_motion().size());
for (size_t i = 0; i < ob->get_motion().size(); ++i) {
// Scale
srt_data[i].sx = decomp[i].y.w; // scale.x.x
srt_data[i].sy = decomp[i].z.w; // scale.y.y
srt_data[i].sz = decomp[i].w.w; // scale.z.z
// Shear
srt_data[i].a = decomp[i].z.x; // scale.x.y
srt_data[i].b = decomp[i].z.y; // scale.x.z
srt_data[i].c = decomp[i].w.x; // scale.y.z
assert(decomp[i].z.z == 0.0f); // scale.y.x
assert(decomp[i].w.y == 0.0f); // scale.z.x
assert(decomp[i].w.z == 0.0f); // scale.z.y
// Pivot point
srt_data[i].pvx = 0.0f;
srt_data[i].pvy = 0.0f;
srt_data[i].pvz = 0.0f;
// Rotation
srt_data[i].qx = decomp[i].x.x;
srt_data[i].qy = decomp[i].x.y;
srt_data[i].qz = decomp[i].x.z;
srt_data[i].qw = decomp[i].x.w;
// Translation
srt_data[i].tx = decomp[i].y.x;
srt_data[i].ty = decomp[i].y.y;
srt_data[i].tz = decomp[i].y.z;
}
// Upload motion transform to GPU
cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
// Disable instance transform if object uses motion transform already
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
// Get traversable handle to motion transform
optixConvertPointerToTraversableHandle(context,
motion_transform_gpu,
OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
&instance.traversableHandle);
}
else {
// Disable instance transform if geometry already has it applied to vertex data
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
// Non-instanced objects read ID from prim_object, so
// distinguish them from instanced objects with high bit set
instance.instanceId |= 0x800000;
instance.traversableHandle = handle;
if (ob->get_geometry()->is_instanced()) {
// Set transform matrix
memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
}
else {
// Disable instance transform if geometry already has it applied to vertex data
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
// Non-instanced objects read ID from prim_object, so
// distinguish them from instanced objects with high bit set
instance.instanceId |= 0x800000;
}
}
}
}
// Upload instance descriptions
// Upload instance descriptions
# if OPTIX_ABI_VERSION < 41
aabbs.resize(num_instances);
aabbs.copy_to_device();
aabbs.resize(num_instances);
aabbs.copy_to_device();
# endif
instances.resize(num_instances);
instances.copy_to_device();
instances.resize(num_instances);
instances.copy_to_device();
// Build top-level acceleration structure (TLAS)
OptixBuildInput build_input = {};
build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
// Build top-level acceleration structure (TLAS)
OptixBuildInput build_input = {};
build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
# if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2
build_input.instanceArray.aabbs = aabbs.device_pointer;
build_input.instanceArray.numAabbs = num_instances;
build_input.instanceArray.aabbs = aabbs.device_pointer;
build_input.instanceArray.numAabbs = num_instances;
# endif
build_input.instanceArray.instances = instances.device_pointer;
build_input.instanceArray.numInstances = num_instances;
build_input.instanceArray.instances = instances.device_pointer;
build_input.instanceArray.numInstances = num_instances;
CUdeviceptr out_data = 0;
tlas_handle = 0;
return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD);
if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
tlas_handle = bvh_optix->traversable_handle;
}
}
void const_copy_to(const char *name, void *host, size_t size) override
@ -1716,7 +1707,7 @@ class OptiXDevice : public CUDADevice {
if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData));
// Fix traversable handle on multi devices
// Update traversable handle (since it is different for each device on multi devices)
KernelData *const data = (KernelData *)host;
*(OptixTraversableHandle *)&data->bvh.scene = tlas_handle;

View File

@ -780,13 +780,25 @@ bool OpenCLInfo::device_supported(const string &platform_name, const cl_device_i
return true;
}
/* It is possible to have Iris GPU on AMD/Apple OpenCL framework
* (aka, it will not be on Intel framework). This isn't supported
* and needs an explicit blacklist.
*/
if (strstr(device_name.c_str(), "Iris")) {
/* Allow Intel GPUs on Intel OpenCL platform. */
if (platform_name.find("Intel") != string::npos) {
if (device_type != CL_DEVICE_TYPE_GPU) {
/* OpenCL on Intel CPU is not an officially supported configuration.
* Use hybrid CPU+GPU rendering to utilize both GPU and CPU. */
return false;
}
# ifdef __APPLE__
/* Apple uses own framework, which can also put Iris onto AMD frame-work.
* This isn't supported configuration. */
return false;
# else
if (device_name.find("Iris") != string::npos || device_name.find("Xe") != string::npos) {
return true;
}
# endif
}
if (platform_name == "AMD Accelerated Parallel Processing" &&
device_type == CL_DEVICE_TYPE_GPU) {
if (driver_major < 2236) {

View File

@ -112,8 +112,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg,
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0]));
isect->prim = hit->primID +
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) +
kernel_tex_fetch(__object_node, hit->instID[0] / 2);
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
isect->object = hit->instID[0] / 2;
}
else {
@ -137,8 +136,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg,
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2));
isect->prim = hit->primID +
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) +
kernel_tex_fetch(__object_node, local_object_id);
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
isect->object = local_object_id;
isect->type = kernel_tex_fetch(__prim_type, isect->prim);
}

View File

@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout {
BVH_LAYOUT_BVH2 = (1 << 0),
BVH_LAYOUT_EMBREE = (1 << 1),
BVH_LAYOUT_OPTIX = (1 << 2),
BVH_LAYOUT_MULTI_OPTIX = (1 << 3),
BVH_LAYOUT_MULTI_OPTIX_EMBREE = (1 << 4),
/* Default BVH layout to use for CPU. */
BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE,
BVH_LAYOUT_ALL = (unsigned int)(~0u),
BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX,
} KernelBVHLayout;
typedef struct KernelBVH {

View File

@ -118,12 +118,18 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
return optixIgnoreIntersection();
}
const uint max_hits = optixGetPayload_5();
if (max_hits == 0) {
// Special case for when no hit information is requested, just report that something was hit
optixSetPayload_5(true);
return optixTerminateRay();
}
int hit = 0;
uint *const lcg_state = get_payload_ptr_0<uint>();
LocalIntersection *const local_isect = get_payload_ptr_2<LocalIntersection>();
if (lcg_state) {
const uint max_hits = optixGetPayload_5();
for (int i = min(max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
if (optixGetRayTmax() == local_isect->hits[i].t) {
return optixIgnoreIntersection();

View File

@ -15,8 +15,7 @@
*/
#include "bvh/bvh.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_embree.h"
#include "bvh/bvh2.h"
#include "device/device.h"
@ -41,6 +40,7 @@
#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const
bool Geometry::need_build_bvh(BVHLayout layout) const
{
return !transform_applied || has_surface_bssrdf || layout == BVH_LAYOUT_OPTIX;
return is_instanced() || layout == BVH_LAYOUT_OPTIX || layout == BVH_LAYOUT_MULTI_OPTIX ||
layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE;
}
bool Geometry::is_instanced() const
@ -218,7 +219,7 @@ void Geometry::compute_bvh(
bvh->geometry = geometry;
bvh->objects = objects;
bvh->refit(*progress);
device->build_bvh(bvh, *progress, true);
}
else {
progress->set_status(msg, "Building BVH");
@ -235,7 +236,7 @@ void Geometry::compute_bvh(
delete bvh;
bvh = BVH::create(bparams, geometry, objects, device);
MEM_GUARDED_CALL(progress, bvh->build, *progress);
MEM_GUARDED_CALL(progress, device->build_bvh, bvh, *progress, false);
}
}
@ -1162,25 +1163,66 @@ void GeometryManager::device_update_bvh(Device *device,
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
bvh->build(progress, &device->stats);
delete scene->bvh;
BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
device->build_bvh(bvh, progress, false);
if (progress.get_cancel()) {
#ifdef WITH_EMBREE
if (dscene->data.bvh.scene) {
BVHEmbree::destroy(dscene->data.bvh.scene);
dscene->data.bvh.scene = NULL;
}
#endif
delete bvh;
return;
}
PackedBVH pack;
if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
pack = std::move(static_cast<BVH2 *>(bvh)->pack);
}
else {
progress.set_status("Updating Scene BVH", "Packing BVH primitives");
size_t num_prims = 0;
size_t num_tri_verts = 0;
foreach (Geometry *geom, scene->geometry) {
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
num_prims += mesh->num_triangles();
num_tri_verts += 3 * mesh->num_triangles();
}
else if (geom->is_hair()) {
Hair *hair = static_cast<Hair *>(geom);
num_prims += hair->num_segments();
}
}
pack.root_index = -1;
pack.prim_tri_index.reserve(num_prims);
pack.prim_tri_verts.reserve(num_tri_verts);
pack.prim_type.reserve(num_prims);
pack.prim_index.reserve(num_prims);
pack.prim_object.reserve(num_prims);
pack.prim_visibility.reserve(num_prims);
// Merge visibility flags of all objects and find object index for non-instanced geometry
unordered_map<const Geometry *, pair<int, uint>> geometry_to_object_info;
geometry_to_object_info.reserve(scene->geometry.size());
foreach (Object *ob, scene->objects) {
const Geometry *const geom = ob->get_geometry();
pair<int, uint> &info = geometry_to_object_info[geom];
info.second |= ob->visibility_for_tracing();
if (!geom->is_instanced()) {
info.first = ob->get_device_index();
}
}
// Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated
// based on that list, which may be ordered differently from the object list.
foreach (Geometry *geom, scene->geometry) {
const pair<int, uint> &info = geometry_to_object_info[geom];
geom->pack_primitives(pack, info.first, info.second);
}
}
/* copy to device */
progress.set_status("Updating Scene BVH", "Copying BVH to device");
PackedBVH &pack = bvh->pack;
if (pack.nodes.size()) {
dscene->bvh_nodes.steal_data(pack.nodes);
dscene->bvh_nodes.copy_to_device();
@ -1226,10 +1268,8 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->data.bvh.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
bvh->copy_to_device(progress, dscene);
delete bvh;
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
dscene->data.bvh.scene = NULL;
}
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
@ -1653,14 +1693,6 @@ void GeometryManager::device_update(Device *device,
void GeometryManager::device_free(Device *device, DeviceScene *dscene)
{
#ifdef WITH_EMBREE
if (dscene->data.bvh.scene) {
if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE)
BVHEmbree::destroy(dscene->data.bvh.scene);
dscene->data.bvh.scene = NULL;
}
#endif
dscene->bvh_nodes.free();
dscene->bvh_leaf_nodes.free();
dscene->object_node.free();

View File

@ -41,6 +41,7 @@ class Scene;
class SceneParams;
class Shader;
class Volume;
struct PackedBVH;
/* Geometry
*
@ -124,6 +125,8 @@ class Geometry : public Node {
int n,
int total);
virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0;
/* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if:
*

View File

@ -14,8 +14,10 @@
* limitations under the License.
*/
#include "render/hair.h"
#include "bvh/bvh.h"
#include "render/curves.h"
#include "render/hair.h"
#include "render/scene.h"
CCL_NAMESPACE_BEGIN
@ -492,4 +494,35 @@ void Hair::pack_curves(Scene *scene,
}
}
void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility)
{
if (curve_first_key.empty())
return;
const size_t num_prims = num_segments();
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
// 'pack.prim_time' is unused by Embree and OptiX
uint type = has_motion_blur() ?
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
PRIMITIVE_MOTION_CURVE_THICK) :
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
for (size_t j = 0; j < num_curves(); ++j) {
Curve curve = get_curve(j);
for (size_t k = 0; k < curve.num_segments(); ++k) {
pack.prim_tri_index.push_back_reserved(-1);
pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
pack.prim_visibility.push_back_reserved(visibility);
// Each curve segment points back to its curve index
pack.prim_index.push_back_reserved(j + prim_offset);
pack.prim_object.push_back_reserved(object);
}
}
}
CCL_NAMESPACE_END

View File

@ -145,6 +145,8 @@ class Hair : public Geometry {
/* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
};
CCL_NAMESPACE_END

View File

@ -493,8 +493,8 @@ static bool image_associate_alpha(ImageManager::Image *img)
template<TypeDesc::BASETYPE FileFormat, typename StorageType>
bool ImageManager::file_load_image(Image *img, int texture_limit)
{
/* we only handle certain number of components */
if (!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) {
/* Ignore empty images. */
if (!(img->metadata.channels > 0)) {
return false;
}

View File

@ -277,6 +277,20 @@ void Mesh::reserve_subd_creases(size_t num_creases)
subd_creases_weight.reserve(num_creases);
}
void Mesh::clear_non_sockets()
{
Geometry::clear(true);
num_subd_verts = 0;
num_subd_faces = 0;
vert_to_stitching_key_map.clear();
vert_stitching_map.clear();
delete patch_table;
patch_table = NULL;
}
void Mesh::clear(bool preserve_shaders, bool preserve_voxel_data)
{
Geometry::clear(preserve_shaders);
@ -297,22 +311,15 @@ void Mesh::clear(bool preserve_shaders, bool preserve_voxel_data)
subd_ptex_offset.clear();
subd_face_corners.clear();
num_subd_verts = 0;
num_subd_faces = 0;
subd_creases_edge.clear();
subd_creases_weight.clear();
subd_attributes.clear();
attributes.clear(preserve_voxel_data);
vert_to_stitching_key_map.clear();
vert_stitching_map.clear();
subdivision_type = SubdivisionType::SUBDIVISION_NONE;
delete patch_table;
patch_table = NULL;
clear_non_sockets();
}
void Mesh::clear(bool preserve_shaders)
@ -662,7 +669,7 @@ void Mesh::add_undisplaced()
float3 *data = attr->data_float3();
/* copy verts */
size_t size = attr->buffer_size(this, attrs.prim);
size_t size = attr->buffer_size(this, ATTR_PRIM_GEOMETRY);
/* Center points for ngons aren't stored in Mesh::verts but are included in size since they will
* be calculated later, we subtract them from size here so we don't have an overflow while
@ -798,4 +805,35 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility)
{
if (triangles.empty())
return;
const size_t num_prims = num_triangles();
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3);
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
// 'pack.prim_time' is unused by Embree and OptiX
uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
for (size_t k = 0; k < num_prims; ++k) {
pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size());
const Mesh::Triangle t = get_triangle(k);
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]]));
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]]));
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]]));
pack.prim_type.push_back_reserved(type);
pack.prim_visibility.push_back_reserved(visibility);
pack.prim_index.push_back_reserved(k + prim_offset);
pack.prim_object.push_back_reserved(object);
}
}
CCL_NAMESPACE_END

View File

@ -184,9 +184,8 @@ class Mesh : public Geometry {
unordered_multimap<int, int>
vert_stitching_map; /* stitching index -> multiple real vert indices */
friend class BVH;
friend class BVH2;
friend class BVHBuild;
friend class BVHEmbree;
friend class BVHSpatialSplit;
friend class DiagSplit;
friend class EdgeDice;
@ -205,6 +204,7 @@ class Mesh : public Geometry {
void resize_subd_faces(int numfaces, int num_ngons, int numcorners);
void reserve_subd_faces(int numfaces, int num_ngons, int numcorners);
void reserve_subd_creases(size_t num_creases);
void clear_non_sockets();
void clear(bool preserve_shaders = false) override;
void add_vertex(float3 P);
void add_vertex_slow(float3 P);
@ -232,6 +232,8 @@ class Mesh : public Geometry {
size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
void tessellate(DiagSplit *split);
SubdFace get_subd_face(size_t index) const;

View File

@ -16,6 +16,7 @@
#include <stdlib.h>
#include "bvh/bvh.h"
#include "device/device.h"
#include "render/background.h"
#include "render/bake.h"
@ -100,6 +101,7 @@ Scene::Scene(const SceneParams &params_, Device *device)
{
memset((void *)&dscene.data, 0, sizeof(dscene.data));
bvh = NULL;
camera = create_node<Camera>();
dicing_camera = create_node<Camera>();
lookup_tables = new LookupTables();
@ -135,6 +137,9 @@ Scene::~Scene()
void Scene::free_memory(bool final)
{
delete bvh;
bvh = NULL;
foreach (Shader *s, shaders)
delete s;
foreach (Geometry *g, geometry)

View File

@ -38,6 +38,7 @@ CCL_NAMESPACE_BEGIN
class AttributeRequestSet;
class Background;
class BVH;
class Camera;
class Device;
class DeviceInfo;
@ -220,6 +221,7 @@ class Scene : public NodeOwner {
string name;
/* data */
BVH *bvh;
Camera *camera;
Camera *dicing_camera;
LookupTables *lookup_tables;

View File

@ -347,8 +347,15 @@ void Shader::tag_update(Scene *scene)
foreach (ShaderNode *node, graph->nodes)
node->attributes(this, &attributes);
if (has_displacement && displacement_method == DISPLACE_BOTH) {
attributes.add(ATTR_STD_POSITION_UNDISPLACED);
if (has_displacement) {
if (displacement_method == DISPLACE_BOTH) {
attributes.add(ATTR_STD_POSITION_UNDISPLACED);
}
if (displacement_method_is_modified()) {
need_update_geometry = true;
scene->geometry_manager->need_update = true;
scene->object_manager->need_flags_update = true;
}
}
/* compare if the attributes changed, mesh manager will check

View File

@ -142,7 +142,11 @@ if(WITH_HEADLESS OR WITH_GHOST_SDL)
list(APPEND INC_SYS
${SDL_INCLUDE_DIR}
)
if(NOT WITH_SDL_DYNLOAD)
if(WITH_SDL_DYNLOAD)
list(APPEND LIB
extern_sdlew
)
else()
list(APPEND LIB
${SDL_LIBRARY}
)
@ -183,6 +187,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
${X11_X11_INCLUDE_PATH}
)
list(APPEND LIB
${X11_X11_LIB}
${X11_Xrender_LIB}
)
list(APPEND SRC
intern/GHOST_DisplayManagerX11.cpp
intern/GHOST_SystemX11.cpp
@ -234,6 +243,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
list(APPEND INC_SYS
${X11_xf86vmode_INCLUDE_PATH}
)
list(APPEND LIB
${X11_Xf86vmode_LIB}
)
endif()
if(WITH_X11_XFIXES)
@ -241,6 +253,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
list(APPEND INC_SYS
${X11_Xfixes_INCLUDE_PATH}
)
list(APPEND LIB
${X11_Xfixes_LIB}
)
endif()
if(WITH_X11_ALPHA)
@ -252,6 +267,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
list(APPEND INC_SYS
${X11_Xinput_INCLUDE_PATH}
)
list(APPEND LIB
${X11_Xinput_LIB}
)
endif()
add_definitions(-DWITH_GHOST_X11)

View File

@ -1052,7 +1052,7 @@ int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_context);
/**
* Check if \a xr_context has a session that requires an upside-down frame-buffer (compared to
* OpenGL). If true, the render result should be flipped vertically for correct output.
* \note: Only to be called after session start, may otherwise result in a false negative.
* \note Only to be called after session start, may otherwise result in a false negative.
*/
int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context);

View File

@ -77,6 +77,12 @@ class GHOST_ISystemPaths {
*/
virtual const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const = 0;
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
*/
virtual const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0;
/**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir

View File

@ -56,6 +56,12 @@ extern const GHOST_TUns8 *GHOST_getSystemDir(int version, const char *versionstr
*/
extern const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr);
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
*/
extern const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type);
/**
* Determine the dir in which the binary file is found.
* \return Unsigned char string pointing to binary dir (eg ~/usr/local/bin/).

View File

@ -565,6 +565,16 @@ typedef struct {
char is_repeat;
} GHOST_TEventKeyData;
typedef enum {
GHOST_kUserSpecialDirDesktop,
GHOST_kUserSpecialDirDocuments,
GHOST_kUserSpecialDirDownloads,
GHOST_kUserSpecialDirMusic,
GHOST_kUserSpecialDirPictures,
GHOST_kUserSpecialDirVideos,
/* Can be extended as needed. */
} GHOST_TUserSpecialDirTypes;
typedef struct {
/** Number of pixels on a line. */
GHOST_TUns32 xPixels;

View File

@ -50,6 +50,12 @@ const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr)
return systemPaths ? systemPaths->getUserDir(version, versionstr) : NULL; /* shouldn't be NULL */
}
const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
{
GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
return systemPaths ? systemPaths->getUserSpecialDir(type) : NULL; /* shouldn't be NULL */
}
const GHOST_TUns8 *GHOST_getBinaryDir()
{
GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();

View File

@ -82,8 +82,8 @@ static GHOST_TButtonMask convertButton(int button)
/**
* Converts Mac rawkey codes (same for Cocoa & Carbon)
* into GHOST key codes
* \param rawCode The raw physical key code
* \param recvChar the character ignoring modifiers (except for shift)
* \param rawCode: The raw physical key code
* \param recvChar: the character ignoring modifiers (except for shift)
* \return Ghost key code
*/
static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
@ -783,7 +783,7 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSet
/**
* Dispose of a context.
* \param context Pointer to the context to be disposed.
* \param context: Pointer to the context to be disposed.
* \return Indication of success.
*/
GHOST_TSuccess GHOST_SystemCocoa::disposeContext(GHOST_IContext *context)
@ -1730,13 +1730,14 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
}
window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
NSPoint delta = [[cocoawindow contentView] convertPointToBacking:NSMakePoint(dx, dy)];
pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000,
window,
GHOST_kTrackpadEventScroll,
x,
y,
dx,
dy,
delta.x,
delta.y,
[event isDirectionInvertedFromDevice]));
}
} break;

View File

@ -55,6 +55,12 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
*/
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
*/
const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
/**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir

View File

@ -21,6 +21,7 @@
#import <Foundation/Foundation.h>
#include "GHOST_Debug.h"
#include "GHOST_SystemPathsCocoa.h"
#pragma mark initialization/finalization
@ -89,6 +90,57 @@ const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserDir(int, const char *versionst
return (GHOST_TUns8 *)tempPath;
}
const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
{
static char tempPath[512] = "";
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *basePath;
NSArray *paths;
NSSearchPathDirectory ns_directory;
switch (type) {
case GHOST_kUserSpecialDirDesktop:
ns_directory = NSDesktopDirectory;
break;
case GHOST_kUserSpecialDirDocuments:
ns_directory = NSDocumentDirectory;
break;
case GHOST_kUserSpecialDirDownloads:
ns_directory = NSDownloadsDirectory;
break;
case GHOST_kUserSpecialDirMusic:
ns_directory = NSMusicDirectory;
break;
case GHOST_kUserSpecialDirPictures:
ns_directory = NSPicturesDirectory;
break;
case GHOST_kUserSpecialDirVideos:
ns_directory = NSMoviesDirectory;
break;
default:
GHOST_ASSERT(
false,
"GHOST_SystemPathsCocoa::getUserSpecialDir(): Invalid enum value for type parameter");
[pool drain];
return NULL;
}
paths = NSSearchPathForDirectoriesInDomains(ns_directory, NSUserDomainMask, YES);
if ([paths count] > 0)
basePath = [paths objectAtIndex:0];
else {
[pool drain];
return NULL;
}
strncpy(
(char *)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding], sizeof(tempPath));
[pool drain];
return (GHOST_TUns8 *)tempPath;
}
const GHOST_TUns8 *GHOST_SystemPathsCocoa::getBinaryDir() const
{
static GHOST_TUns8 tempPath[512] = "";

View File

@ -21,6 +21,9 @@
* \ingroup GHOST
*/
#include <cstdio>
#include <sstream>
#include "GHOST_SystemPathsUnix.h"
#include "GHOST_Debug.h"
@ -108,6 +111,62 @@ const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserDir(int version, const char *ve
}
}
const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
{
const char *type_str;
switch (type) {
case GHOST_kUserSpecialDirDesktop:
type_str = "DESKTOP";
break;
case GHOST_kUserSpecialDirDocuments:
type_str = "DOCUMENTS";
break;
case GHOST_kUserSpecialDirDownloads:
type_str = "DOWNLOAD";
break;
case GHOST_kUserSpecialDirMusic:
type_str = "MUSIC";
break;
case GHOST_kUserSpecialDirPictures:
type_str = "PICTURES";
break;
case GHOST_kUserSpecialDirVideos:
type_str = "VIDEOS";
break;
default:
GHOST_ASSERT(
false,
"GHOST_SystemPathsUnix::getUserSpecialDir(): Invalid enum value for type parameter");
return NULL;
}
static string path = "";
/* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */
string command = string("xdg-user-dir ") + type_str + " 2> /dev/null";
FILE *fstream = popen(command.c_str(), "r");
if (fstream == NULL) {
return NULL;
}
std::stringstream path_stream;
while (!feof(fstream)) {
char c = fgetc(fstream);
/* xdg-user-dir ends the path with '\n'. */
if (c == '\n') {
break;
}
path_stream << c;
}
if (pclose(fstream) == -1) {
perror("GHOST_SystemPathsUnix::getUserSpecialDir failed at pclose()");
return NULL;
}
path = path_stream.str();
return path[0] ? (const GHOST_TUns8 *)path.c_str() : NULL;
}
const GHOST_TUns8 *GHOST_SystemPathsUnix::getBinaryDir() const
{
return NULL;

View File

@ -53,6 +53,12 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
*/
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
*/
const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
/**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir

View File

@ -22,6 +22,7 @@
*/
#include "GHOST_SystemPathsWin32.h"
#include "GHOST_Debug.h"
#ifndef _WIN32_IE
# define _WIN32_IE 0x0501
@ -76,6 +77,50 @@ const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserDir(int, const char *versionst
return NULL;
}
const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
{
GUID folderid;
switch (type) {
case GHOST_kUserSpecialDirDesktop:
folderid = FOLDERID_Desktop;
break;
case GHOST_kUserSpecialDirDocuments:
folderid = FOLDERID_Documents;
break;
case GHOST_kUserSpecialDirDownloads:
folderid = FOLDERID_Downloads;
break;
case GHOST_kUserSpecialDirMusic:
folderid = FOLDERID_Music;
break;
case GHOST_kUserSpecialDirPictures:
folderid = FOLDERID_Pictures;
break;
case GHOST_kUserSpecialDirVideos:
folderid = FOLDERID_Videos;
break;
default:
GHOST_ASSERT(
false,
"GHOST_SystemPathsWin32::getUserSpecialDir(): Invalid enum value for type parameter");
return NULL;
}
static char knownpath[MAX_PATH * 3] = {0};
PWSTR knownpath_16 = NULL;
HRESULT hResult = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, NULL, &knownpath_16);
if (hResult == S_OK) {
conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3);
CoTaskMemFree(knownpath_16);
return (GHOST_TUns8 *)knownpath;
}
CoTaskMemFree(knownpath_16);
return NULL;
}
const GHOST_TUns8 *GHOST_SystemPathsWin32::getBinaryDir() const
{
static char fullname[MAX_PATH * 3] = {0};

View File

@ -62,6 +62,12 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
*/
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
/**
* Determine a special ("well known") and easy to reach user directory.
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
*/
const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
/**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir

View File

@ -422,7 +422,7 @@ finished:
/**
* Dispose of a context.
* \param context Pointer to the context to be disposed.
* \param context: Pointer to the context to be disposed.
* \return Indication of success.
*/
GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
@ -1151,6 +1151,8 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
GHOST_TInt32 x_screen, y_screen;
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
GHOST_TabletData tabletData = GHOST_TABLET_DATA_NONE;
if (window->m_tabletInRange || window->wintabSysButPressed()) {
if (window->useTabletAPI(GHOST_kTabletWintab) &&
processWintabEvent(
@ -1164,8 +1166,13 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
return NULL;
}
/* If using Wintab but no button event is currently active,
* fall through to default handling. */
/* If using Wintab but no button event is currently active, fall through to default handling.
*
* Populate tablet data so that cursor is recognized as an absolute position device. */
tabletData.Active = GHOST_kTabletModeStylus;
tabletData.Pressure = 0.0f;
tabletData.Xtilt = 0.0f;
tabletData.Ytilt = 0.0f;
}
system->getCursorPosition(x_screen, y_screen);
@ -1198,16 +1205,12 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
window,
x_screen + x_accum,
y_screen + y_accum,
GHOST_TABLET_DATA_NONE);
tabletData);
}
}
else {
return new GHOST_EventCursor(system->getMilliSeconds(),
GHOST_kEventCursorMove,
window,
x_screen,
y_screen,
GHOST_TABLET_DATA_NONE);
return new GHOST_EventCursor(
system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen, tabletData);
}
return NULL;
}
@ -1614,7 +1617,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break;
}
case WT_PACKET:
window->updatePendingWintabEvents();
window->updateWintabEventsSyncTime();
break;
////////////////////////////////////////////////////////////////////////
// Pointer events, processed

View File

@ -407,7 +407,7 @@ void GHOST_WindowWayland::setOpaque() const
#endif
/**
* \param type The type of rendering context create.
* \param type: The type of rendering context create.
* \return Indication of success.
*/
GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type)

View File

@ -1324,7 +1324,7 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
updatePendingWintabEvents();
updateWintabEvents();
auto &pendingEvents = m_wintab.pendingEvents;
size_t pendingEventSize = pendingEvents.size();
@ -1420,6 +1420,44 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
return GHOST_kSuccess;
}
void GHOST_WindowWin32::updateWintabEvents()
{
readWintabEvents();
// When a Wintab device is used to leave window focus, some of it's packets are periodically not
// queued in time to be flushed. Reading packets needs to occur before expiring packets to clear
// these from the queue.
expireWintabEvents();
}
void GHOST_WindowWin32::updateWintabEventsSyncTime()
{
readWintabEvents();
if (!m_wintab.pendingEvents.empty()) {
auto lastEvent = m_wintab.pendingEvents.back();
m_wintab.sysTimeOffset = ::GetTickCount() - lastEvent.pkTime;
}
expireWintabEvents();
}
void GHOST_WindowWin32::readWintabEvents()
{
if (!(m_wintab.packetsGet && m_wintab.context)) {
return;
}
auto &pendingEvents = m_wintab.pendingEvents;
/* Get new packets. */
const int numPackets = m_wintab.packetsGet(
m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
for (int i = 0; i < numPackets; i++) {
pendingEvents.push(m_wintab.pkts[i]);
}
}
/* Wintab (per documentation but may vary with implementation) does not update when its event
* buffer is full. This is an issue because we need some synchronization point between Wintab
* events and Win32 events, so we can't drain and process the queue immediately. We need to
@ -1428,17 +1466,12 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
* mode from the Wintab API alone. There is no guaranteed ordering between Wintab and Win32 mouse
* events and no documented time stamp shared between the two, so we synchronize on mouse button
* events. */
void GHOST_WindowWin32::updatePendingWintabEvents()
void GHOST_WindowWin32::expireWintabEvents()
{
if (!(m_wintab.packetsGet && m_wintab.context)) {
return;
}
auto &pendingEvents = m_wintab.pendingEvents;
/* Clear outdated events from queue. */
DWORD currTime = ::GetTickCount();
DWORD millisTimeout = 500;
DWORD currTime = ::GetTickCount() - m_wintab.sysTimeOffset;
DWORD millisTimeout = 300;
while (!pendingEvents.empty()) {
DWORD pkTime = pendingEvents.front().pkTime;
@ -1449,24 +1482,6 @@ void GHOST_WindowWin32::updatePendingWintabEvents()
break;
}
}
/* Get new packets. */
const int numPackets = m_wintab.packetsGet(
m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
int i = 0;
/* Don't queue outdated packets, such events can include packets that occurred before the current
* window lost and regained focus. */
for (; i < numPackets; i++) {
DWORD pkTime = m_wintab.pkts[i].pkTime;
if (currTime < pkTime + millisTimeout) {
break;
}
}
for (; i < numPackets; i++) {
pendingEvents.push(m_wintab.pkts[i]);
}
}
GHOST_TUns16 GHOST_WindowWin32::getDPIHint()

View File

@ -486,9 +486,14 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
/**
* Updates stored pending Wintab events.
* Updates pending Wintab events and syncs Wintab time with OS time.
*/
void updatePendingWintabEvents();
void updateWintabEventsSyncTime();
/**
* Updates pending Wintab events.
*/
void updateWintabEvents();
GHOST_TSuccess beginFullScreen() const
{
@ -629,6 +634,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TUns8 numSysButtons = 0;
/** Cursors currently in contact mapped to system buttons */
DWORD sysButtonsPressed = 0;
DWORD sysTimeOffset = 0;
LONG maxPressure = 0;
LONG maxAzimuth = 0, maxAltitude = 0;
/** Reusable buffer to read in Wintab Packets. */
@ -642,6 +648,10 @@ class GHOST_WindowWin32 : public GHOST_Window {
*/
void initializeWintab();
void readWintabEvents();
void expireWintabEvents();
/**
* Convert Wintab system mapped (mouse) buttons into Ghost button mask.
* \param cursor: The Wintab cursor associated to the button.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More