From 370a2d6917ebd8df5177fe4854a447e3839dddaa Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 26 Feb 2018 16:39:18 +0100 Subject: [PATCH 01/21] Fix T53857: Incorrect framerate for videos imported from OBS This is an issue with which value to trust: fps vs. tbr. They both cam be somewhat broken. Currently the idea is: - If file was saved with FFmpeg AND we are decoding with FFmpeg we trust tbr. - If we are decoding with Libav we use fps (there does not seem to be tbr in Libav, unless i'm missing something). - All other cases we use fps. Seems to work all good for files from T53857, T54148 and T51153. Ideally we would need to collect some amount of regression files to make further tweaks more scientific. Reviewers: mont29 Reviewed By: mont29 Differential Revision: https://developer.blender.org/D3083 --- intern/ffmpeg/ffmpeg_compat.h | 49 ++++++++++++++++++- source/blender/imbuf/intern/anim_movie.c | 4 +- source/blender/imbuf/intern/indexer.c | 2 +- .../gameengine/VideoTexture/VideoFFmpeg.cpp | 2 +- 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 9c06c8a6d67..1eb6c3ba2dc 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -48,6 +48,16 @@ #include +/* Stupid way to distinguish FFmpeg from Libav: + * - FFmpeg's MICRO version starts from 100 and goes up, while + * - Libav's micro is always below 100. + */ +#if LIBAVCODEC_VERSION_MICRO >= 100 +# define AV_USING_FFMPEG +#else +# define AV_USING_LIBAV +#endif + #if (LIBAVFORMAT_VERSION_MAJOR > 52) || ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 105)) # define FFMPEG_HAVE_AVIO 1 #endif @@ -428,8 +438,45 @@ void av_frame_free(AVFrame **frame) #endif FFMPEG_INLINE -AVRational av_get_r_frame_rate_compat(const AVStream *stream) +const char* av_get_metadata_key_value(AVDictionary *metadata, const char *key) { + if (metadata == NULL) { + return NULL; + } + AVDictionaryEntry *tag = NULL; + while ((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if (!strcmp(tag->key, key)) { + return tag->value; + } + } + return NULL; +} + +FFMPEG_INLINE +bool av_check_encoded_with_ffmpeg(AVFormatContext *ctx) +{ + const char* encoder = av_get_metadata_key_value(ctx->metadata, "ENCODER"); + if (encoder != NULL && !strncmp(encoder, "Lavf", 4)) { + return true; + } + return false; +} + +FFMPEG_INLINE +AVRational av_get_r_frame_rate_compat(AVFormatContext *ctx, + const AVStream *stream) +{ + /* If the video is encoded with FFmpeg and we are decoding with FFmpeg + * as well it seems to be more reliable to use r_frame_rate (tbr). + * + * For other cases we fall back to avg_frame_rate (fps) when possible. + */ +#ifdef AV_USING_FFMPEG + if (av_check_encoded_with_ffmpeg(ctx)) { + return stream->r_frame_rate; + } +#endif + #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 23, 1) /* For until r_frame_rate was deprecated use it. */ return stream->r_frame_rate; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 5472cae3ef2..a770b34ecc6 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -511,7 +511,7 @@ static int startffmpeg(struct anim *anim) return -1; } - frame_rate = av_get_r_frame_rate_compat(pFormatCtx->streams[videoStream]); + frame_rate = av_get_r_frame_rate_compat(pFormatCtx, pFormatCtx->streams[videoStream]); if (pFormatCtx->streams[videoStream]->nb_frames != 0) { anim->duration = pFormatCtx->streams[videoStream]->nb_frames; } @@ -989,7 +989,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, v_st = anim->pFormatCtx->streams[anim->videoStream]; - frame_rate = av_q2d(av_get_r_frame_rate_compat(v_st)); + frame_rate = av_q2d(av_get_r_frame_rate_compat(anim->pFormatCtx, v_st)); st_time = anim->pFormatCtx->start_time; pts_time_base = av_q2d(v_st->time_base); diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 009258079ee..eaf4dfd84b4 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -909,7 +909,7 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, stream_size = avio_size(context->iFormatCtx->pb); - context->frame_rate = av_q2d(av_get_r_frame_rate_compat(context->iStream)); + context->frame_rate = av_q2d(av_get_r_frame_rate_compat(context->iFormatCtx, context->iStream)); context->pts_time_base = av_q2d(context->iStream->time_base); while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) { diff --git a/source/gameengine/VideoTexture/VideoFFmpeg.cpp b/source/gameengine/VideoTexture/VideoFFmpeg.cpp index 10196804656..11ec97ca5f8 100644 --- a/source/gameengine/VideoTexture/VideoFFmpeg.cpp +++ b/source/gameengine/VideoTexture/VideoFFmpeg.cpp @@ -228,7 +228,7 @@ int VideoFFmpeg::openStream(const char *filename, AVInputFormat *inputFormat, AV codecCtx->frame_rate_base=1000; m_baseFrameRate = (double)codecCtx->frame_rate / (double)codecCtx->frame_rate_base; #else - m_baseFrameRate = av_q2d(av_get_r_frame_rate_compat(formatCtx->streams[videoStream])); + m_baseFrameRate = av_q2d(av_get_r_frame_rate_compat(formatCtx, formatCtx->streams[videoStream])); #endif if (m_baseFrameRate <= 0.0) m_baseFrameRate = defFrameRate; From 40e62f271c137f6bce19c6f6497948d014a7ab17 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 5 Mar 2018 15:32:49 +0100 Subject: [PATCH 02/21] FFmpeg: Add regression tests Currently only covering handful of files from reports about wrong fps detected. It will need D3083 applied first to get tests passed, also tests themselves are to be committed to svn. But there are some python code which needs to be reviewed, like blendfile passed to run_blender(). Reviewers: sybren, mont29 Reviewed By: sybren, mont29 Subscribers: mont29 Differential Revision: https://developer.blender.org/D3096 --- tests/python/CMakeLists.txt | 10 ++++ tests/python/ffmpeg_tests.py | 86 ++++++++++++++++++++++++++++++ tests/python/modules/test_utils.py | 12 +++-- 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100755 tests/python/ffmpeg_tests.py diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 6419c3f36e2..69761249002 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -27,6 +27,7 @@ set(USE_EXPERIMENTAL_TESTS FALSE) set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) +set(TEST_DATA_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests_data) set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) # ugh, any better way to do this on testing only? @@ -617,4 +618,13 @@ if(WITH_ALEMBIC) ) endif() +if(WITH_CODEC_FFMPEG) + add_python_test( + ffmpeg_tests + ${CMAKE_CURRENT_LIST_DIR}/ffmpeg_tests.py + --blender "$" + --testdir "${TEST_DATA_SRC_DIR}/ffmpeg" + ) +endif() + add_subdirectory(collada) diff --git a/tests/python/ffmpeg_tests.py b/tests/python/ffmpeg_tests.py new file mode 100755 index 00000000000..9493d6f7a17 --- /dev/null +++ b/tests/python/ffmpeg_tests.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +import argparse +import functools +import shutil +import pathlib +import subprocess +import sys +import tempfile +import unittest + +from modules.test_utils import AbstractBlenderRunnerTest + + +class AbstractFFmpegTest(AbstractBlenderRunnerTest): + @classmethod + def setUpClass(cls): + cls.blender = args.blender + cls.testdir = pathlib.Path(args.testdir) + + +class AbstractFFmpegSequencerTest(AbstractFFmpegTest): + def get_script_for_file(self, filename: pathlib.Path) -> str: + movie = self.testdir / filename + return \ + "import bpy; " \ + "bpy.context.scene.sequence_editor_create(); " \ + "strip = bpy.context.scene.sequence_editor.sequences.new_movie(" \ + "'test_movie', %r, channel=1, frame_start=1); " \ + "print(f'fps:{strip.fps}')" % movie.as_posix() + + def get_movie_file_fps(self, filename: pathlib.Path) -> float: + script = self.get_script_for_file(filename) + output = self.run_blender('', script) + for line in output.splitlines(): + if line.startswith('fps:'): + return float(line.split(':')[1]) + return 0.0 + + +class FPSDetectionTest(AbstractFFmpegSequencerTest): + def test_T51153(self): + self.assertAlmostEqual( + self.get_movie_file_fps('T51153_bad_clip_2.mts'), + 29.97, + places=2) + + def test_T53857(self): + self.assertAlmostEqual( + self.get_movie_file_fps('T53857_2018-01-22_15-30-49.mkv'), + 30.0, + places=2) + + def test_T54148(self): + self.assertAlmostEqual( + self.get_movie_file_fps('T54148_magn_0.mkv'), + 1.0, + places=2) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--blender', required=True) + parser.add_argument('--testdir', required=True) + args, remaining = parser.parse_known_args() + + unittest.main(argv=sys.argv[0:1] + remaining) diff --git a/tests/python/modules/test_utils.py b/tests/python/modules/test_utils.py index 6ca498d8cdf..55ef882c49c 100755 --- a/tests/python/modules/test_utils.py +++ b/tests/python/modules/test_utils.py @@ -75,18 +75,24 @@ class AbstractBlenderRunnerTest(unittest.TestCase): assert self.blender, "Path to Blender binary is to be set in setUpClass()" assert self.testdir, "Path to tests binary is to be set in setUpClass()" - blendfile = self.testdir / filepath + blendfile = self.testdir / filepath if filepath else "" - command = ( + command = [ self.blender, '--background', '-noaudio', '--factory-startup', '--enable-autoexec', - str(blendfile), + ] + + if blendfile: + command.append(str(blendfile)) + + command.extend([ '-E', 'CYCLES', '--python-exit-code', '47', '--python-expr', python_script, + ] ) proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, From 14e7ba0c8ac7f229a1b025103a786f267dfb9387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 9 Mar 2018 19:03:29 +0100 Subject: [PATCH 03/21] Fix Pose Lib: pose is applied when selected bones don't overlap with pose Premise: When pose bones are selected, applying a pose library should only affect the selected bones. This commit fixes a bug where the pose was also applied when there was no overlap between the selected bones and the bones in the pose. For example, applying a pose which contains only keyframes for the left hand, while only right-hand bones are selected, would apply the pose to the left hand anyway. The code is now also slightly more efficient; the removed 'selcount' counter was only used as a binary (i.e. zero or non-zero). It's now stored as a bitflag instead. --- source/blender/editors/armature/pose_lib.c | 34 +++++++++++++++------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 587ae6991a2..d9d5fa395d6 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -833,7 +833,6 @@ typedef struct tPoseLib_PreviewData { bAction *act; /* poselib to use */ TimeMarker *marker; /* 'active' pose */ - int selcount; /* number of selected elements to work on */ int totcount; /* total number of elements to work on */ short state; /* state of main loop */ @@ -866,7 +865,8 @@ enum { /* defines for tPoseLib_PreviewData->flag values */ enum { PL_PREVIEW_FIRSTTIME = (1 << 0), - PL_PREVIEW_SHOWORIGINAL = (1 << 1) + PL_PREVIEW_SHOWORIGINAL = (1 << 1), + PL_PREVIEW_ANY_BONE_SELECTED = (1 << 2), }; /* ---------------------------- */ @@ -886,7 +886,20 @@ static void poselib_backup_posecopy(tPoseLib_PreviewData *pld) { bActionGroup *agrp; bPoseChannel *pchan; - + bool selected = false; + + /* determine whether any bone is selected. */ + LISTBASE_FOREACH (bPoseChannel *, bchan, &pld->pose->chanbase) { + selected = bchan->bone != NULL && bchan->bone->flag & BONE_SELECTED; + if (selected) { + pld->flag |= PL_PREVIEW_ANY_BONE_SELECTED; + break; + } + } + if (!selected) { + pld->flag &= ~PL_PREVIEW_ANY_BONE_SELECTED; + } + /* for each posechannel that has an actionchannel in */ for (agrp = pld->act->groups.first; agrp; agrp = agrp->next) { /* try to find posechannel */ @@ -908,8 +921,6 @@ static void poselib_backup_posecopy(tPoseLib_PreviewData *pld) BLI_addtail(&pld->backups, plb); /* mark as being affected */ - if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) - pld->selcount++; pld->totcount++; } } @@ -970,6 +981,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld) KeyframeEditData ked = {{NULL}}; KeyframeEditFunc group_ok_cb; int frame = 1; + const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED; /* get the frame */ if (pld->marker) @@ -982,8 +994,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld) group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE); ked.f1 = ((float)frame) - 0.5f; ked.f2 = ((float)frame) + 0.5f; - - + /* start applying - only those channels which have a key at this point in time! */ for (agrp = act->groups.first; agrp; agrp = agrp->next) { /* check if group has any keyframes */ @@ -995,7 +1006,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld) bool ok = 0; /* check if this bone should get any animation applied */ - if (pld->selcount == 0) { + if (!any_bone_selected) { /* if no bones are selected, then any bone is ok */ ok = 1; } @@ -1008,7 +1019,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld) ok = 1; } } - + if (ok) animsys_evaluate_action_group(ptr, act, agrp, NULL, (float)frame); } @@ -1027,14 +1038,15 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); ListBase dsources = {NULL, NULL}; bool autokey = autokeyframe_cfra_can_key(scene, &pld->ob->id); - + const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED; + /* start tagging/keying */ for (agrp = act->groups.first; agrp; agrp = agrp->next) { /* only for selected bones unless there aren't any selected, in which case all are included */ pchan = BKE_pose_channel_find_name(pose, agrp->name); if (pchan) { - if ((pld->selcount == 0) || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { + if (!any_bone_selected || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { if (autokey) { /* add datasource override for the PoseChannel, to be used later */ ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan); From 41b38c5f75b371e3897685fd9a4cb953a4aa65ff Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 9 Mar 2018 21:17:43 +0100 Subject: [PATCH 04/21] Don't add "Report a Bug" button for official tracker to non-official add-ons If no custom URL was set, add-ons would get a "Report a Bug" button opening the default developer.blender.org bug tracker. Now we only add this default button if the add-on is bundled and not installed by the user. --- release/scripts/startup/bl_ui/space_userpref.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index ce3d6fffbb3..e61a6abbbca 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1513,12 +1513,15 @@ class USERPREF_PT_addons(Panel): split.operator( "wm.url_open", text="Documentation", icon='HELP', ).url = info["wiki_url"] - split.operator( - "wm.url_open", text="Report a Bug", icon='URL', - ).url = info.get( - "tracker_url", - "https://developer.blender.org/maniphest/task/edit/form/2", - ) + # Only add "Report a Bug" button if tracker_url is set + # or the add-on is bundled (use official tracker then). + if info.get("tracker_url") or not user_addon: + split.operator( + "wm.url_open", text="Report a Bug", icon='URL', + ).url = info.get( + "tracker_url", + "https://developer.blender.org/maniphest/task/edit/form/2", + ) if user_addon: split.operator( "wm.addon_remove", text="Remove", icon='CANCEL', From f66ff4ee86a9ead9ef6f3776b64f4dda32eab521 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 7 Mar 2018 22:19:56 +0100 Subject: [PATCH 05/21] Code refactor: use KernelOject struct instead of float4 array. Original patch by Stefan with modifications by Brecht. --- intern/cycles/kernel/geom/geom_attribute.h | 4 +- intern/cycles/kernel/geom/geom_object.h | 83 +++++++--------------- intern/cycles/kernel/kernel_textures.h | 2 +- intern/cycles/kernel/kernel_types.h | 25 ++++++- intern/cycles/render/object.cpp | 46 ++++++------ intern/cycles/render/object.h | 2 +- intern/cycles/render/scene.h | 2 +- 7 files changed, 79 insertions(+), 85 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h index c72595eed10..6e2ee3b62d7 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/geom_attribute.h @@ -53,9 +53,7 @@ ccl_device_inline AttributeDescriptor attribute_not_found() ccl_device_inline uint object_attribute_map_offset(KernelGlobals *kg, int object) { - int offset = object*OBJECT_SIZE + 15; - float4 f = kernel_tex_fetch(__objects, offset); - return __float_as_uint(f.y); + return kernel_tex_fetch(__objects, object).attribute_map_offset; } ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id) diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 32aa2007f5d..6bb4d134b99 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -29,11 +29,6 @@ CCL_NAMESPACE_BEGIN enum ObjectTransform { OBJECT_TRANSFORM = 0, OBJECT_INVERSE_TRANSFORM = 4, - OBJECT_TRANSFORM_MOTION_PRE = 0, - OBJECT_TRANSFORM_MOTION_MID = 4, - OBJECT_TRANSFORM_MOTION_POST = 8, - OBJECT_PROPERTIES = 12, - OBJECT_DUPLI = 13 }; enum ObjectVectorTransform { @@ -45,12 +40,17 @@ enum ObjectVectorTransform { ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object, enum ObjectTransform type) { - int offset = object*OBJECT_SIZE + (int)type; - Transform tfm; - tfm.x = kernel_tex_fetch(__objects, offset + 0); - tfm.y = kernel_tex_fetch(__objects, offset + 1); - tfm.z = kernel_tex_fetch(__objects, offset + 2); + if(type == OBJECT_INVERSE_TRANSFORM) { + tfm.x = kernel_tex_fetch(__objects, object).tfm.mid.x; + tfm.y = kernel_tex_fetch(__objects, object).tfm.mid.y; + tfm.z = kernel_tex_fetch(__objects, object).tfm.mid.z; + } + else { + tfm.x = kernel_tex_fetch(__objects, object).tfm.pre.x; + tfm.y = kernel_tex_fetch(__objects, object).tfm.pre.y; + tfm.z = kernel_tex_fetch(__objects, object).tfm.pre.z; + } tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); return tfm; @@ -91,24 +91,7 @@ ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int #ifdef __OBJECT_MOTION__ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int object, float time) { - MotionTransform motion; - - int offset = object*OBJECT_SIZE + (int)OBJECT_TRANSFORM_MOTION_PRE; - - motion.pre.x = kernel_tex_fetch(__objects, offset + 0); - motion.pre.y = kernel_tex_fetch(__objects, offset + 1); - motion.pre.z = kernel_tex_fetch(__objects, offset + 2); - motion.pre.w = kernel_tex_fetch(__objects, offset + 3); - - motion.mid.x = kernel_tex_fetch(__objects, offset + 4); - motion.mid.y = kernel_tex_fetch(__objects, offset + 5); - motion.mid.z = kernel_tex_fetch(__objects, offset + 6); - motion.mid.w = kernel_tex_fetch(__objects, offset + 7); - - motion.post.x = kernel_tex_fetch(__objects, offset + 8); - motion.post.y = kernel_tex_fetch(__objects, offset + 9); - motion.post.z = kernel_tex_fetch(__objects, offset + 10); - motion.post.w = kernel_tex_fetch(__objects, offset + 11); + MotionTransform motion = kernel_tex_fetch(__objects, object).tfm; Transform tfm; transform_motion_interpolate(&tfm, &motion, time); @@ -237,9 +220,7 @@ ccl_device_inline float3 object_location(KernelGlobals *kg, const ShaderData *sd ccl_device_inline float object_surface_area(KernelGlobals *kg, int object) { - int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES; - float4 f = kernel_tex_fetch(__objects, offset); - return f.x; + return kernel_tex_fetch(__objects, object).surface_area; } /* Pass ID number of object */ @@ -249,9 +230,7 @@ ccl_device_inline float object_pass_id(KernelGlobals *kg, int object) if(object == OBJECT_NONE) return 0.0f; - int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES; - float4 f = kernel_tex_fetch(__objects, offset); - return f.y; + return kernel_tex_fetch(__objects, object).pass_id; } /* Per lamp random number for shader variation */ @@ -272,9 +251,7 @@ ccl_device_inline float object_random_number(KernelGlobals *kg, int object) if(object == OBJECT_NONE) return 0.0f; - int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES; - float4 f = kernel_tex_fetch(__objects, offset); - return f.z; + return kernel_tex_fetch(__objects, object).random_number; } /* Particle ID from which this object was generated */ @@ -284,9 +261,7 @@ ccl_device_inline int object_particle_id(KernelGlobals *kg, int object) if(object == OBJECT_NONE) return 0; - int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES; - float4 f = kernel_tex_fetch(__objects, offset); - return __float_as_uint(f.w); + return kernel_tex_fetch(__objects, object).particle_index; } /* Generated texture coordinate on surface from where object was instanced */ @@ -296,9 +271,10 @@ ccl_device_inline float3 object_dupli_generated(KernelGlobals *kg, int object) if(object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - int offset = object*OBJECT_SIZE + OBJECT_DUPLI; - float4 f = kernel_tex_fetch(__objects, offset); - return make_float3(f.x, f.y, f.z); + const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + return make_float3(kobject->dupli_generated[0], + kobject->dupli_generated[1], + kobject->dupli_generated[2]); } /* UV texture coordinate on surface from where object was instanced */ @@ -308,27 +284,24 @@ ccl_device_inline float3 object_dupli_uv(KernelGlobals *kg, int object) if(object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - int offset = object*OBJECT_SIZE + OBJECT_DUPLI; - float4 f = kernel_tex_fetch(__objects, offset + 1); - return make_float3(f.x, f.y, 0.0f); + const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + return make_float3(kobject->dupli_uv[0], + kobject->dupli_uv[1], + 0.0f); } /* Information about mesh for motion blurred triangles and curves */ ccl_device_inline void object_motion_info(KernelGlobals *kg, int object, int *numsteps, int *numverts, int *numkeys) { - int offset = object*OBJECT_SIZE + OBJECT_DUPLI; - if(numkeys) { - float4 f = kernel_tex_fetch(__objects, offset); - *numkeys = __float_as_int(f.w); + *numkeys = kernel_tex_fetch(__objects, object).numkeys; } - float4 f = kernel_tex_fetch(__objects, offset + 1); if(numsteps) - *numsteps = __float_as_int(f.z); + *numsteps = kernel_tex_fetch(__objects, object).numsteps; if(numverts) - *numverts = __float_as_int(f.w); + *numverts = kernel_tex_fetch(__objects, object).numverts; } /* Offset to an objects patch map */ @@ -338,9 +311,7 @@ ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) if(object == OBJECT_NONE) return 0; - int offset = object*OBJECT_SIZE + 15; - float4 f = kernel_tex_fetch(__objects, offset); - return __float_as_uint(f.x); + return kernel_tex_fetch(__objects, object).patch_map_offset; } /* Pass ID for shader */ diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 74b659557e5..427ed142e7f 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -31,7 +31,7 @@ KERNEL_TEX(uint, __object_node) KERNEL_TEX(float2, __prim_time) /* objects */ -KERNEL_TEX(float4, __objects) +KERNEL_TEX(KernelObject, __objects) KERNEL_TEX(float4, __objects_vector) /* triangles */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 2a437cdbdc6..b6f1a3a6d98 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -35,7 +35,6 @@ CCL_NAMESPACE_BEGIN /* Constants */ -#define OBJECT_SIZE 16 #define OBJECT_VECTOR_SIZE 6 #define LIGHT_SIZE 11 #define FILTER_TABLE_SIZE 1024 @@ -1434,6 +1433,30 @@ typedef struct KernelData { } KernelData; static_assert_align(KernelData, 16); +/* Kernel data structures. */ + +typedef struct KernelObject { + MotionTransform tfm; + + float surface_area; + float pass_id; + float random_number; + int particle_index; + + float dupli_generated[3]; + float dupli_uv[2]; + + int numkeys; + int numsteps; + int numverts; + + uint patch_map_offset; + uint attribute_map_offset; + uint pad1, pad2; +} KernelObject;; +static_assert_align(KernelObject, 16); + + /* Declarations required for split kernel */ /* Macro for queues */ diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index b981d2b8849..c992469cb39 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -289,7 +289,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s Object *ob, int object_index) { - float4 *objects = state->objects; + KernelObject& kobject = state->objects[object_index]; float4 *objects_vector = state->objects_vector; Mesh *mesh = ob->mesh; @@ -357,15 +357,15 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s } } - /* Pack in texture. */ - int offset = object_index*OBJECT_SIZE; - /* OBJECT_TRANSFORM */ - memcpy(&objects[offset], &tfm, sizeof(float4)*3); + memcpy(&kobject.tfm.pre, &tfm, sizeof(float4)*3); /* OBJECT_INVERSE_TRANSFORM */ - memcpy(&objects[offset+4], &itfm, sizeof(float4)*3); + memcpy(&kobject.tfm.mid, &itfm, sizeof(float4)*3); /* OBJECT_PROPERTIES */ - objects[offset+12] = make_float4(surface_area, pass_id, random_number, __int_as_float(particle_index)); + kobject.surface_area = surface_area; + kobject.pass_id = pass_id; + kobject.random_number = random_number; + kobject.particle_index = particle_index; if(mesh->use_motion_blur) { state->have_motion = true; @@ -404,21 +404,24 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s MotionTransform decomp; transform_motion_decompose(&decomp, &ob->motion, &ob->tfm); - memcpy(&objects[offset], &decomp, sizeof(float4)*12); + kobject.tfm = decomp; flag |= SD_OBJECT_MOTION; state->have_motion = true; } } /* Dupli object coords and motion info. */ + kobject.dupli_generated[0] = ob->dupli_generated[0]; + kobject.dupli_generated[1] = ob->dupli_generated[1]; + kobject.dupli_generated[2] = ob->dupli_generated[2]; + kobject.numkeys = mesh->curve_keys.size(); + kobject.dupli_uv[0] = ob->dupli_uv[0]; + kobject.dupli_uv[1] = ob->dupli_uv[1]; int totalsteps = mesh->motion_steps; - int numsteps = (totalsteps - 1)/2; - int numverts = mesh->verts.size(); - int numkeys = mesh->curve_keys.size(); - - objects[offset+13] = make_float4(ob->dupli_generated[0], ob->dupli_generated[1], ob->dupli_generated[2], __int_as_float(numkeys)); - objects[offset+14] = make_float4(ob->dupli_uv[0], ob->dupli_uv[1], __int_as_float(numsteps), __int_as_float(numverts)); - objects[offset+15] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + kobject.numsteps = (totalsteps - 1)/2; + kobject.numverts = mesh->verts.size();; + kobject.patch_map_offset = 0; + kobject.attribute_map_offset = 0; /* Object flag. */ if(ob->use_holdout) { @@ -486,7 +489,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, state.queue_start_object = 0; state.object_flag = object_flag; - state.objects = dscene->objects.alloc(OBJECT_SIZE*scene->objects.size()); + state.objects = dscene->objects.alloc(scene->objects.size()); if(state.need_motion == Scene::MOTION_PASS) { state.objects_vector = dscene->objects_vector.alloc(OBJECT_VECTOR_SIZE*scene->objects.size()); } @@ -652,27 +655,26 @@ void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Sc return; } - uint4* objects = (uint4*)dscene->objects.data(); + KernelObject *kobjects = dscene->objects.data(); bool update = false; int object_index = 0; foreach(Object *object, scene->objects) { Mesh* mesh = object->mesh; - int offset = object_index*OBJECT_SIZE + 15; if(mesh->patch_table) { uint patch_map_offset = 2*(mesh->patch_table_offset + mesh->patch_table->total_size() - mesh->patch_table->num_nodes * PATCH_NODE_SIZE) - mesh->patch_offset; - if(objects[offset].x != patch_map_offset) { - objects[offset].x = patch_map_offset; + if(kobjects[object_index].patch_map_offset != patch_map_offset) { + kobjects[object_index].patch_map_offset = patch_map_offset; update = true; } } - if(objects[offset].y != mesh->attr_map_offset) { - objects[offset].y = mesh->attr_map_offset; + if(kobjects[object_index].attribute_map_offset != mesh->attr_map_offset) { + kobjects[object_index].attribute_map_offset = mesh->attr_map_offset; update = true; } diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index acdb1b64123..54046d7069f 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -134,7 +134,7 @@ protected: /* Packed object arrays. Those will be filled in. */ uint *object_flag; - float4 *objects; + KernelObject *objects; float4 *objects_vector; /* Flags which will be synchronized to Integrator. */ diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index ea9485ff230..48e9ae4117a 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -86,7 +86,7 @@ public: device_vector patches; /* objects */ - device_vector objects; + device_vector objects; device_vector objects_vector; /* attributes */ From fa9175ff02acffd02679398e17732f3832d3fc28 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Thu, 8 Mar 2018 00:15:41 +0100 Subject: [PATCH 06/21] Code refactor: use KernelLight instead of float4 arrays. Original patch by Stefan with modifications by Brecht. --- intern/cycles/kernel/geom/geom_object.h | 18 +-- intern/cycles/kernel/kernel_light.h | 148 ++++++++++++------------ intern/cycles/kernel/kernel_textures.h | 4 +- intern/cycles/kernel/kernel_types.h | 56 ++++++++- intern/cycles/render/light.cpp | 136 +++++++++++++--------- intern/cycles/render/scene.cpp | 2 +- intern/cycles/render/scene.h | 4 +- 7 files changed, 232 insertions(+), 136 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 6bb4d134b99..9d833b77bdb 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -60,12 +60,17 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse) { - int offset = lamp*LIGHT_SIZE + (inverse? 8 : 5); - Transform tfm; - tfm.x = kernel_tex_fetch(__light_data, offset + 0); - tfm.y = kernel_tex_fetch(__light_data, offset + 1); - tfm.z = kernel_tex_fetch(__light_data, offset + 2); + if(inverse) { + tfm.x = kernel_tex_fetch(__lights, lamp).itfm[0]; + tfm.y = kernel_tex_fetch(__lights, lamp).itfm[1]; + tfm.z = kernel_tex_fetch(__lights, lamp).itfm[2]; + } + else { + tfm.x = kernel_tex_fetch(__lights, lamp).tfm[0]; + tfm.y = kernel_tex_fetch(__lights, lamp).tfm[1]; + tfm.z = kernel_tex_fetch(__lights, lamp).tfm[2]; + } tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); return tfm; @@ -240,8 +245,7 @@ ccl_device_inline float lamp_random_number(KernelGlobals *kg, int lamp) if(lamp == LAMP_NONE) return 0.0f; - float4 f = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 4); - return f.y; + return kernel_tex_fetch(__lights, lamp).random; } /* Per object random number for shader variation */ diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index dfa3150dc92..aaf7a7abdd4 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -255,11 +255,11 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side(KernelGlobals float3 *lightpos, float3 *dir) { - float4 data0 = kernel_tex_fetch(__light_data, (index + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 0); - float4 data3 = kernel_tex_fetch(__light_data, (index + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 3); + int portal = kernel_data.integrator.portal_offset + index; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); - *lightpos = make_float3(data0.y, data0.z, data0.w); - *dir = make_float3(data3.y, data3.z, data3.w); + *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]); + *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); /* Check whether portal is on the right side. */ if(dot(*dir, P - *lightpos) > 1e-4f) @@ -291,11 +291,10 @@ ccl_device_inline float background_portal_pdf(KernelGlobals *kg, } num_possible++; - float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1); - float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2); - - float3 axisu = make_float3(data1.y, data1.z, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); + int portal = kernel_data.integrator.portal_offset + p; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); + float3 axisu = make_float3(klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); + float3 axisv = make_float3(klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL, NULL, NULL)) continue; @@ -346,10 +345,10 @@ ccl_device float3 background_portal_sample(KernelGlobals *kg, if(portal == 0) { /* p is the portal to be sampled. */ - float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1); - float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2); - float3 axisu = make_float3(data1.y, data1.z, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); + int portal = kernel_data.integrator.portal_offset + p; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); + float3 axisu = make_float3(klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); + float3 axisv = make_float3(klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); *pdf = area_light_sample(P, &lightpos, axisu, axisv, @@ -479,14 +478,10 @@ ccl_device float3 sphere_light_sample(float3 P, float3 center, float radius, flo return disk_light_sample(normalize(P - center), randu, randv)*radius; } -ccl_device float spot_light_attenuation(float4 data1, float4 data2, LightSample *ls) +ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot_smooth, LightSample *ls) { - float3 dir = make_float3(data2.y, data2.z, data2.w); float3 I = ls->Ng; - float spot_angle = data1.w; - float spot_smooth = data2.x; - float attenuation = dot(dir, I); if(attenuation <= spot_angle) { @@ -518,12 +513,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, float3 P, LightSample *ls) { - float4 data0 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 0); - float4 data1 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 1); - - LightType type = (LightType)__float_as_int(data0.x); + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + LightType type = (LightType)klight->type; ls->type = type; - ls->shader = __float_as_int(data1.x); + ls->shader = klight->shader_id; ls->object = PRIM_NONE; ls->prim = PRIM_NONE; ls->lamp = lamp; @@ -532,10 +525,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, if(type == LIGHT_DISTANT) { /* distant light */ - float3 lightD = make_float3(data0.y, data0.z, data0.w); + float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); float3 D = lightD; - float radius = data1.y; - float invarea = data1.w; + float radius = klight->distant.radius; + float invarea = klight->distant.invarea; if(radius > 0.0f) D = distant_light_sample(D, radius, randu, randv); @@ -562,10 +555,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, } #endif else { - ls->P = make_float3(data0.y, data0.z, data0.w); + ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]); if(type == LIGHT_POINT || type == LIGHT_SPOT) { - float radius = data1.y; + float radius = klight->spot.radius; if(radius > 0.0f) /* sphere light */ @@ -574,14 +567,19 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, ls->D = normalize_len(ls->P - P, &ls->t); ls->Ng = -ls->D; - float invarea = data1.z; + float invarea = klight->spot.invarea; ls->eval_fac = (0.25f*M_1_PI_F)*invarea; ls->pdf = invarea; if(type == LIGHT_SPOT) { /* spot light attenuation */ - float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); - ls->eval_fac *= spot_light_attenuation(data1, data2, ls); + float3 dir = make_float3(klight->spot.dir[0], + klight->spot.dir[1], + klight->spot.dir[2]); + ls->eval_fac *= spot_light_attenuation(dir, + klight->spot.spot_angle, + klight->spot.spot_smooth, + ls); if(ls->eval_fac == 0.0f) { return false; } @@ -594,12 +592,15 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, } else { /* area light */ - float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); - float4 data3 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 3); - - float3 axisu = make_float3(data1.y, data1.z, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); - float3 D = make_float3(data3.y, data3.z, data3.w); + float3 axisu = make_float3(klight->area.axisu[0], + klight->area.axisu[1], + klight->area.axisu[2]); + float3 axisv = make_float3(klight->area.axisv[0], + klight->area.axisv[1], + klight->area.axisv[2]); + float3 D = make_float3(klight->area.dir[0], + klight->area.dir[1], + klight->area.dir[2]); if(dot(ls->P - P, D) > 0.0f) { return false; @@ -618,7 +619,7 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, ls->Ng = D; ls->D = normalize_len(ls->P - P, &ls->t); - float invarea = data2.x; + float invarea = klight->area.invarea; ls->eval_fac = 0.25f*invarea; } } @@ -630,12 +631,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls) { - float4 data0 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 0); - float4 data1 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 1); - - LightType type = (LightType)__float_as_int(data0.x); + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + LightType type = (LightType)klight->type; ls->type = type; - ls->shader = __float_as_int(data1.x); + ls->shader = klight->shader_id; ls->object = PRIM_NONE; ls->prim = PRIM_NONE; ls->lamp = lamp; @@ -648,7 +647,7 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, if(type == LIGHT_DISTANT) { /* distant light */ - float radius = data1.y; + float radius = klight->distant.radius; if(radius == 0.0f) return false; @@ -670,9 +669,9 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, * P */ - float3 lightD = make_float3(data0.y, data0.z, data0.w); + float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); float costheta = dot(-lightD, D); - float cosangle = data1.z; + float cosangle = klight->distant.cosangle; if(costheta < cosangle) return false; @@ -683,13 +682,14 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, ls->t = FLT_MAX; /* compute pdf */ - float invarea = data1.w; + float invarea = klight->distant.invarea; ls->pdf = invarea/(costheta*costheta*costheta); ls->eval_fac = ls->pdf; } else if(type == LIGHT_POINT || type == LIGHT_SPOT) { - float3 lightP = make_float3(data0.y, data0.z, data0.w); - float radius = data1.y; + float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); + + float radius = klight->spot.radius; /* sphere light */ if(radius == 0.0f) @@ -704,14 +704,19 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, ls->Ng = -D; ls->D = D; - float invarea = data1.z; + float invarea = klight->spot.invarea; ls->eval_fac = (0.25f*M_1_PI_F)*invarea; ls->pdf = invarea; if(type == LIGHT_SPOT) { /* spot light attenuation */ - float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); - ls->eval_fac *= spot_light_attenuation(data1, data2, ls); + float3 dir = make_float3(klight->spot.dir[0], + klight->spot.dir[1], + klight->spot.dir[2]); + ls->eval_fac *= spot_light_attenuation(dir, + klight->spot.spot_angle, + klight->spot.spot_smooth, + ls); if(ls->eval_fac == 0.0f) return false; @@ -726,22 +731,25 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, } else if(type == LIGHT_AREA) { /* area light */ - float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); - float4 data3 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 3); - - float invarea = data2.x; + float invarea = klight->area.invarea; if(invarea == 0.0f) return false; - float3 axisu = make_float3(data1.y, data1.z, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); - float3 Ng = make_float3(data3.y, data3.z, data3.w); + float3 axisu = make_float3(klight->area.axisu[0], + klight->area.axisu[1], + klight->area.axisu[2]); + float3 axisv = make_float3(klight->area.axisv[0], + klight->area.axisv[1], + klight->area.axisv[2]); + float3 Ng = make_float3(klight->area.dir[0], + klight->area.dir[1], + klight->area.dir[2]); /* one sided */ if(dot(D, Ng) >= 0.0f) return false; - float3 light_P = make_float3(data0.y, data0.z, data0.w); + float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); if(!ray_quad_intersect(P, D, 0.0f, t, light_P, axisu, axisv, Ng, @@ -1040,7 +1048,7 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) int half_len = len >> 1; int middle = first + half_len; - if(r < kernel_tex_fetch(__light_distribution, middle).x) { + if(r < kernel_tex_fetch(__light_distribution, middle).totarea) { len = half_len; } else { @@ -1055,8 +1063,8 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) /* Rescale to reuse random number. this helps the 2D samples within * each area light be stratified as well. */ - float distr_min = kernel_tex_fetch(__light_distribution, index).x; - float distr_max = kernel_tex_fetch(__light_distribution, index+1).x; + float distr_min = kernel_tex_fetch(__light_distribution, index).totarea; + float distr_max = kernel_tex_fetch(__light_distribution, index+1).totarea; *randu = (r - distr_min)/(distr_max - distr_min); return index; @@ -1066,8 +1074,7 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) ccl_device bool light_select_reached_max_bounces(KernelGlobals *kg, int index, int bounce) { - float4 data4 = kernel_tex_fetch(__light_data, index*LIGHT_SIZE + 4); - return (bounce > __float_as_int(data4.x)); + return (bounce > kernel_tex_fetch(__lights, index).max_bounces); } ccl_device_noinline bool light_sample(KernelGlobals *kg, @@ -1082,12 +1089,12 @@ ccl_device_noinline bool light_sample(KernelGlobals *kg, int index = light_distribution_sample(kg, &randu); /* fetch light data */ - float4 l = kernel_tex_fetch(__light_distribution, index); - int prim = __float_as_int(l.y); + const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution, index); + int prim = kdistribution->prim; if(prim >= 0) { - int object = __float_as_int(l.w); - int shader_flag = __float_as_int(l.z); + int object = kdistribution->mesh_light.object_id; + int shader_flag = kdistribution->mesh_light.shader_flag; triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); ls->shader |= shader_flag; @@ -1106,8 +1113,7 @@ ccl_device_noinline bool light_sample(KernelGlobals *kg, ccl_device int light_select_num_samples(KernelGlobals *kg, int index) { - float4 data3 = kernel_tex_fetch(__light_data, index*LIGHT_SIZE + 3); - return __float_as_int(data3.x); + return kernel_tex_fetch(__lights, index).samples; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 427ed142e7f..601165a3848 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -55,8 +55,8 @@ KERNEL_TEX(float4, __attributes_float3) KERNEL_TEX(uchar4, __attributes_uchar4) /* lights */ -KERNEL_TEX(float4, __light_distribution) -KERNEL_TEX(float4, __light_data) +KERNEL_TEX(KernelLightDistribution, __light_distribution) +KERNEL_TEX(KernelLight, __lights) KERNEL_TEX(float2, __light_background_marginal_cdf) KERNEL_TEX(float2, __light_background_conditional_cdf) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index b6f1a3a6d98..198ce39c63f 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -36,7 +36,6 @@ CCL_NAMESPACE_BEGIN /* Constants */ #define OBJECT_VECTOR_SIZE 6 -#define LIGHT_SIZE 11 #define FILTER_TABLE_SIZE 1024 #define RAMP_TABLE_SIZE 256 #define SHUTTER_TABLE_SIZE 256 @@ -1456,6 +1455,61 @@ typedef struct KernelObject { } KernelObject;; static_assert_align(KernelObject, 16); +typedef struct KernelSpotLight { + float radius; + float invarea; + float spot_angle; + float spot_smooth; + float dir[3]; +} KernelSpotLight; + +/* PointLight is SpotLight with only radius and invarea being used. */ + +typedef struct KernelAreaLight { + float axisu[3]; + float invarea; + float axisv[3]; + float dir[3]; +} KernelAreaLight; + +typedef struct KernelDistantLight { + float radius; + float cosangle; + float invarea; +} KernelDistantLight; + +typedef struct KernelLight { + int type; + float co[3]; + int shader_id; + int samples; + float max_bounces; + float random; + float4 tfm[3]; + float4 itfm[3]; + union { + KernelSpotLight spot; + KernelAreaLight area; + KernelDistantLight distant; + }; +} KernelLight; +static_assert_align(KernelLight, 16); + +typedef struct KernelLightDistribution { + float totarea; + int prim; + union { + struct { + int shader_flag; + int object_id; + } mesh_light; + struct { + float pad; + float size; + } lamp; + }; +} KernelLightDistribution; +static_assert_align(KernelLightDistribution, 16); /* Declarations required for split kernel */ diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index b62453cf5fc..a3f67361f6e 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -288,7 +288,7 @@ void LightManager::device_update_distribution(Device *, DeviceScene *dscene, Sce VLOG(1) << "Total " << num_distribution << " of light distribution primitives."; /* emission area */ - float4 *distribution = dscene->light_distribution.alloc(num_distribution + 1); + KernelLightDistribution *distribution = dscene->light_distribution.alloc(num_distribution + 1); float totarea = 0.0f; /* triangles */ @@ -334,10 +334,10 @@ void LightManager::device_update_distribution(Device *, DeviceScene *dscene, Sce : scene->default_surface; if(shader->use_mis && shader->has_surface_emission) { - distribution[offset].x = totarea; - distribution[offset].y = __int_as_float(i + mesh->tri_offset); - distribution[offset].z = __int_as_float(shader_flag); - distribution[offset].w = __int_as_float(object_id); + distribution[offset].totarea = totarea; + distribution[offset].prim = i + mesh->tri_offset; + distribution[offset].mesh_light.shader_flag = shader_flag; + distribution[offset].mesh_light.object_id = object_id; offset++; Mesh::Triangle t = mesh->get_triangle(i); @@ -372,10 +372,10 @@ void LightManager::device_update_distribution(Device *, DeviceScene *dscene, Sce if(!light->is_enabled) continue; - distribution[offset].x = totarea; - distribution[offset].y = __int_as_float(~light_index); - distribution[offset].z = 1.0f; - distribution[offset].w = light->size; + distribution[offset].totarea = totarea; + distribution[offset].prim = ~light_index; + distribution[offset].lamp.pad = 1.0f; + distribution[offset].lamp.size = light->size; totarea += lightarea; if(light->size > 0.0f && light->use_mis) @@ -390,15 +390,15 @@ void LightManager::device_update_distribution(Device *, DeviceScene *dscene, Sce } /* normalize cumulative distribution functions */ - distribution[num_distribution].x = totarea; - distribution[num_distribution].y = 0.0f; - distribution[num_distribution].z = 0.0f; - distribution[num_distribution].w = 0.0f; + distribution[num_distribution].totarea = totarea; + distribution[num_distribution].prim = 0.0f; + distribution[num_distribution].lamp.pad = 0.0f; + distribution[num_distribution].lamp.size = 0.0f; if(totarea > 0.0f) { for(size_t i = 0; i < num_distribution; i++) - distribution[i].x /= totarea; - distribution[num_distribution].x = 1.0f; + distribution[i].totarea /= totarea; + distribution[num_distribution].totarea = 1.0f; } if(progress.get_cancel()) return; @@ -620,7 +620,7 @@ void LightManager::device_update_points(Device *, } } - float4 *light_data = dscene->light_data.alloc(num_lights*LIGHT_SIZE); + KernelLight *klights = dscene->lights.alloc(num_lights); if(num_lights == 0) { VLOG(1) << "No effective light, ignoring points update."; @@ -637,8 +637,8 @@ void LightManager::device_update_points(Device *, float3 co = light->co; Shader *shader = (light->shader) ? light->shader : scene->default_light; int shader_id = scene->shader_manager->get_shader_id(shader); - float samples = __int_as_float(light->samples); - float max_bounces = __int_as_float(light->max_bounces); + int samples = light->samples; + int max_bounces = light->max_bounces; float random = (float)light->random_id * (1.0f/(float)0xFFFFFFFF); if(!light->cast_shadow) @@ -661,6 +661,9 @@ void LightManager::device_update_points(Device *, use_light_visibility = true; } + klights[light_index].type = light->type; + klights[light_index].samples = samples; + if(light->type == LIGHT_POINT) { shader_id &= ~SHADER_AREA_LIGHT; @@ -670,10 +673,12 @@ void LightManager::device_update_points(Device *, if(light->use_mis && radius > 0.0f) shader_id |= SHADER_USE_MIS; - light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z); - light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, invarea, 0.0f); - light_data[light_index*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f); + klights[light_index].co[0] = co.x; + klights[light_index].co[1] = co.y; + klights[light_index].co[2] = co.z; + + klights[light_index].spot.radius = radius; + klights[light_index].spot.invarea = invarea; } else if(light->type == LIGHT_DISTANT) { shader_id &= ~SHADER_AREA_LIGHT; @@ -690,10 +695,13 @@ void LightManager::device_update_points(Device *, if(light->use_mis && area > 0.0f) shader_id |= SHADER_USE_MIS; - light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), dir.x, dir.y, dir.z); - light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, cosangle, invarea); - light_data[light_index*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f); + klights[light_index].co[0] = dir.x; + klights[light_index].co[1] = dir.y; + klights[light_index].co[2] = dir.z; + + klights[light_index].distant.invarea = invarea; + klights[light_index].distant.radius = radius; + klights[light_index].distant.cosangle = cosangle; } else if(light->type == LIGHT_BACKGROUND) { uint visibility = scene->background->visibility; @@ -717,11 +725,6 @@ void LightManager::device_update_points(Device *, shader_id |= SHADER_EXCLUDE_SCATTER; use_light_visibility = true; } - - light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), 0.0f, 0.0f, 0.0f); - light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), 0.0f, 0.0f, 0.0f); - light_data[light_index*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f); } else if(light->type == LIGHT_AREA) { float3 axisu = light->axisu*(light->sizeu*light->size); @@ -735,10 +738,20 @@ void LightManager::device_update_points(Device *, if(light->use_mis && area > 0.0f) shader_id |= SHADER_USE_MIS; - light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z); - light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), axisu.x, axisu.y, axisu.z); - light_data[light_index*LIGHT_SIZE + 2] = make_float4(invarea, axisv.x, axisv.y, axisv.z); - light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, dir.x, dir.y, dir.z); + klights[light_index].co[0] = co.x; + klights[light_index].co[1] = co.y; + klights[light_index].co[2] = co.z; + + klights[light_index].area.axisu[0] = axisu.x; + klights[light_index].area.axisu[1] = axisu.y; + klights[light_index].area.axisu[2] = axisu.z; + klights[light_index].area.axisv[0] = axisv.x; + klights[light_index].area.axisv[1] = axisv.y; + klights[light_index].area.axisv[2] = axisv.z; + klights[light_index].area.invarea = invarea; + klights[light_index].area.dir[0] = dir.x; + klights[light_index].area.dir[1] = dir.y; + klights[light_index].area.dir[2] = dir.z; } else if(light->type == LIGHT_SPOT) { shader_id &= ~SHADER_AREA_LIGHT; @@ -754,18 +767,29 @@ void LightManager::device_update_points(Device *, if(light->use_mis && radius > 0.0f) shader_id |= SHADER_USE_MIS; - light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z); - light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, invarea, spot_angle); - light_data[light_index*LIGHT_SIZE + 2] = make_float4(spot_smooth, dir.x, dir.y, dir.z); - light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f); + klights[light_index].co[0] = co.x; + klights[light_index].co[1] = co.y; + klights[light_index].co[2] = co.z; + + klights[light_index].spot.radius = radius; + klights[light_index].spot.invarea = invarea; + klights[light_index].spot.spot_angle = spot_angle; + klights[light_index].spot.spot_smooth = spot_smooth; + klights[light_index].spot.dir[0] = dir.x; + klights[light_index].spot.dir[1] = dir.y; + klights[light_index].spot.dir[2] = dir.z; } - light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, random, 0.0f, 0.0f); + klights[light_index].shader_id = shader_id; + + klights[light_index].max_bounces = max_bounces; + klights[light_index].random = random; + Transform tfm = light->tfm; Transform itfm = transform_inverse(tfm); - memcpy(&light_data[light_index*LIGHT_SIZE + 5], &tfm, sizeof(float4)*3); - memcpy(&light_data[light_index*LIGHT_SIZE + 8], &itfm, sizeof(float4)*3); + memcpy(&klights[light_index].tfm, &tfm, sizeof(float4)*3); + memcpy(&klights[light_index].itfm, &itfm, sizeof(float4)*3); light_index++; } @@ -782,21 +806,29 @@ void LightManager::device_update_points(Device *, float3 axisu = light->axisu*(light->sizeu*light->size); float3 axisv = light->axisv*(light->sizev*light->size); float area = len(axisu)*len(axisv); - float invarea = (area > 0.0f) ? 1.0f / area : 1.0f; + float invarea = (area > 0.0f)? 1.0f/area: 1.0f; float3 dir = light->dir; dir = safe_normalize(dir); - light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z); - light_data[light_index*LIGHT_SIZE + 1] = make_float4(area, axisu.x, axisu.y, axisu.z); - light_data[light_index*LIGHT_SIZE + 2] = make_float4(invarea, axisv.x, axisv.y, axisv.z); - light_data[light_index*LIGHT_SIZE + 3] = make_float4(-1, dir.x, dir.y, dir.z); - light_data[light_index*LIGHT_SIZE + 4] = make_float4(-1, 0.0f, 0.0f, 0.0f); + klights[light_index].co[0] = co.x; + klights[light_index].co[1] = co.y; + klights[light_index].co[2] = co.z; + klights[light_index].area.axisu[0] = axisu.x; + klights[light_index].area.axisu[1] = axisu.y; + klights[light_index].area.axisu[2] = axisu.z; + klights[light_index].area.axisv[0] = axisv.x; + klights[light_index].area.axisv[1] = axisv.y; + klights[light_index].area.axisv[2] = axisv.z; + klights[light_index].area.invarea = invarea; + klights[light_index].area.dir[0] = dir.x; + klights[light_index].area.dir[1] = dir.y; + klights[light_index].area.dir[2] = dir.z; Transform tfm = light->tfm; Transform itfm = transform_inverse(tfm); - memcpy(&light_data[light_index*LIGHT_SIZE + 5], &tfm, sizeof(float4)*3); - memcpy(&light_data[light_index*LIGHT_SIZE + 8], &itfm, sizeof(float4)*3); + memcpy(&klights[light_index].tfm, &tfm, sizeof(float4)*3); + memcpy(&klights[light_index].itfm, &itfm, sizeof(float4)*3); light_index++; } @@ -806,7 +838,7 @@ void LightManager::device_update_points(Device *, VLOG(1) << "Number of lights without contribution: " << num_scene_lights - light_index; - dscene->light_data.copy_to_device(); + dscene->lights.copy_to_device(); } void LightManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress) @@ -842,7 +874,7 @@ void LightManager::device_update(Device *device, DeviceScene *dscene, Scene *sce void LightManager::device_free(Device *, DeviceScene *dscene) { dscene->light_distribution.free(); - dscene->light_data.free(); + dscene->lights.free(); dscene->light_background_marginal_cdf.free(); dscene->light_background_conditional_cdf.free(); } diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 24923b650e3..aca28fc32fb 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -66,7 +66,7 @@ DeviceScene::DeviceScene(Device *device) attributes_float3(device, "__attributes_float3", MEM_TEXTURE), attributes_uchar4(device, "__attributes_uchar4", MEM_TEXTURE), light_distribution(device, "__light_distribution", MEM_TEXTURE), - light_data(device, "__light_data", MEM_TEXTURE), + lights(device, "__lights", MEM_TEXTURE), light_background_marginal_cdf(device, "__light_background_marginal_cdf", MEM_TEXTURE), light_background_conditional_cdf(device, "__light_background_conditional_cdf", MEM_TEXTURE), particles(device, "__particles", MEM_TEXTURE), diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 48e9ae4117a..3f089b9138f 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -96,8 +96,8 @@ public: device_vector attributes_uchar4; /* lights */ - device_vector light_distribution; - device_vector light_data; + device_vector light_distribution; + device_vector lights; device_vector light_background_marginal_cdf; device_vector light_background_conditional_cdf; From f3010e98c343a83e07ff6c2a5437d0043122b083 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Thu, 8 Mar 2018 00:35:24 +0100 Subject: [PATCH 07/21] Code refactor: use KernelShader and KernelParticle instead of float arrays. Original patch by Stefan with modifications by Brecht. --- intern/cycles/kernel/bvh/bvh_shadow_all.h | 2 +- intern/cycles/kernel/bvh/qbvh_shadow_all.h | 2 +- intern/cycles/kernel/geom/geom_object.h | 36 ++++++---------------- intern/cycles/kernel/kernel_emission.h | 8 ++--- intern/cycles/kernel/kernel_shader.h | 12 ++++---- intern/cycles/kernel/kernel_textures.h | 4 +-- intern/cycles/kernel/kernel_types.h | 27 ++++++++++++++-- intern/cycles/kernel/kernel_volume.h | 4 +-- intern/cycles/render/particles.cpp | 22 ++++++------- intern/cycles/render/scene.cpp | 2 +- intern/cycles/render/scene.h | 4 +-- intern/cycles/render/shader.cpp | 21 ++++++------- 12 files changed, 72 insertions(+), 72 deletions(-) diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index efd6798ca51..cfc567ff9ca 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -276,7 +276,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, shader = __float_as_int(str.z); } #endif - int flag = kernel_tex_fetch(__shader_flag, (shader & SHADER_MASK)*SHADER_SIZE); + int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; /* if no transparent shadows, all light is blocked */ if(!(flag & SD_HAS_TRANSPARENT_SHADOW)) { diff --git a/intern/cycles/kernel/bvh/qbvh_shadow_all.h b/intern/cycles/kernel/bvh/qbvh_shadow_all.h index 522213f30ca..46fd178aed6 100644 --- a/intern/cycles/kernel/bvh/qbvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/qbvh_shadow_all.h @@ -358,7 +358,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, shader = __float_as_int(str.z); } #endif - int flag = kernel_tex_fetch(__shader_flag, (shader & SHADER_MASK)*SHADER_SIZE); + int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; /* if no transparent shadows, all light is blocked */ if(!(flag & SD_HAS_TRANSPARENT_SHADOW)) { diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 9d833b77bdb..0b410f448c8 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -322,67 +322,49 @@ ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd) { - return kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE + 1); + return kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).pass_id; } /* Particle data from which object was instanced */ ccl_device_inline uint particle_index(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f = kernel_tex_fetch(__particles, offset + 0); - return __float_as_uint(f.x); + return kernel_tex_fetch(__particles, particle).index; } ccl_device float particle_age(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f = kernel_tex_fetch(__particles, offset + 0); - return f.y; + return kernel_tex_fetch(__particles, particle).age; } ccl_device float particle_lifetime(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f = kernel_tex_fetch(__particles, offset + 0); - return f.z; + return kernel_tex_fetch(__particles, particle).lifetime; } ccl_device float particle_size(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f = kernel_tex_fetch(__particles, offset + 0); - return f.w; + return kernel_tex_fetch(__particles, particle).size; } ccl_device float4 particle_rotation(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f = kernel_tex_fetch(__particles, offset + 1); - return f; + return kernel_tex_fetch(__particles, particle).rotation; } ccl_device float3 particle_location(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f = kernel_tex_fetch(__particles, offset + 2); - return make_float3(f.x, f.y, f.z); + return float4_to_float3(kernel_tex_fetch(__particles, particle).location); } ccl_device float3 particle_velocity(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f2 = kernel_tex_fetch(__particles, offset + 2); - float4 f3 = kernel_tex_fetch(__particles, offset + 3); - return make_float3(f2.w, f3.x, f3.y); + return float4_to_float3(kernel_tex_fetch(__particles, particle).velocity); } ccl_device float3 particle_angular_velocity(KernelGlobals *kg, int particle) { - int offset = particle*PARTICLE_SIZE; - float4 f3 = kernel_tex_fetch(__particles, offset + 3); - float4 f4 = kernel_tex_fetch(__particles, offset + 4); - return make_float3(f3.z, f3.w, f4.x); + return float4_to_float3(kernel_tex_fetch(__particles, particle).angular_velocity); } /* Object intersection in BVH */ diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index 5875249b404..a5556c3be8f 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -29,7 +29,7 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg, /* setup shading at emitter */ float3 eval; - int shader_flag = kernel_tex_fetch(__shader_flag, (ls->shader & SHADER_MASK)*SHADER_SIZE); + int shader_flag = kernel_tex_fetch(__shaders, (ls->shader & SHADER_MASK)).flags; #ifdef __BACKGROUND_MIS__ if(ls->type == LIGHT_BACKGROUND) { @@ -51,9 +51,9 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg, #endif if(shader_flag & SD_HAS_CONSTANT_EMISSION) { - eval.x = __int_as_float(kernel_tex_fetch(__shader_flag, (ls->shader & SHADER_MASK)*SHADER_SIZE + 2)); - eval.y = __int_as_float(kernel_tex_fetch(__shader_flag, (ls->shader & SHADER_MASK)*SHADER_SIZE + 3)); - eval.z = __int_as_float(kernel_tex_fetch(__shader_flag, (ls->shader & SHADER_MASK)*SHADER_SIZE + 4)); + eval.x = kernel_tex_fetch(__shaders, (ls->shader & SHADER_MASK)).constant_emission[0]; + eval.y = kernel_tex_fetch(__shaders, (ls->shader & SHADER_MASK)).constant_emission[1]; + eval.z = kernel_tex_fetch(__shaders, (ls->shader & SHADER_MASK)).constant_emission[2]; if((ls->prim != PRIM_NONE) && dot(ls->Ng, I) < 0.0f) { ls->Ng = -ls->Ng; } diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index b1f66852b7f..fc8d06fc33d 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -114,7 +114,7 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg, sd->I = -ray->D; - sd->flag |= kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE); + sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; #ifdef __INSTANCING__ if(isect->object != OBJECT_NONE) { @@ -199,7 +199,7 @@ void shader_setup_from_subsurface( motion_triangle_shader_setup(kg, sd, isect, ray, true); } - sd->flag |= kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE); + sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; # ifdef __INSTANCING__ if(isect->object != OBJECT_NONE) { @@ -276,7 +276,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, sd->time = time; sd->ray_length = t; - sd->flag = kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE); + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; sd->object_flag = 0; if(sd->object != OBJECT_NONE) { sd->object_flag |= kernel_tex_fetch(__object_flag, @@ -386,7 +386,7 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, ShaderDat sd->Ng = -ray->D; sd->I = -ray->D; sd->shader = kernel_data.background.surface_shader; - sd->flag = kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE); + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; sd->object_flag = 0; sd->time = ray->time; sd->ray_length = 0.0f; @@ -1181,7 +1181,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, sd->shader = stack[i].shader; sd->flag &= ~SD_SHADER_FLAGS; - sd->flag |= kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE); + sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; sd->object_flag &= ~SD_OBJECT_FLAGS; if(sd->object != OBJECT_NONE) { @@ -1254,7 +1254,7 @@ ccl_device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect shader = __float_as_int(str.z); } #endif - int flag = kernel_tex_fetch(__shader_flag, (shader & SHADER_MASK)*SHADER_SIZE); + int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; return (flag & SD_HAS_TRANSPARENT_SHADOW) != 0; } diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 601165a3848..892e0c84cba 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -61,11 +61,11 @@ KERNEL_TEX(float2, __light_background_marginal_cdf) KERNEL_TEX(float2, __light_background_conditional_cdf) /* particles */ -KERNEL_TEX(float4, __particles) +KERNEL_TEX(KernelParticle, __particles) /* shaders */ KERNEL_TEX(uint4, __svm_nodes) -KERNEL_TEX(uint, __shader_flag) +KERNEL_TEX(KernelShader, __shaders) KERNEL_TEX(uint, __object_flag) /* lookup tables */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 198ce39c63f..2cab63cdc6a 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -39,8 +39,6 @@ CCL_NAMESPACE_BEGIN #define FILTER_TABLE_SIZE 1024 #define RAMP_TABLE_SIZE 256 #define SHUTTER_TABLE_SIZE 256 -#define PARTICLE_SIZE 5 -#define SHADER_SIZE 5 #define BSSRDF_MIN_RADIUS 1e-8f #define BSSRDF_MAX_HITS 4 @@ -923,7 +921,7 @@ enum ShaderDataFlag { SD_HAS_BUMP = (1 << 25), /* Has true displacement. */ SD_HAS_DISPLACEMENT = (1 << 26), - /* Has constant emission (value stored in __shader_flag) */ + /* Has constant emission (value stored in __shaders) */ SD_HAS_CONSTANT_EMISSION = (1 << 27), /* Needs to access attributes */ SD_NEED_ATTRIBUTES = (1 << 28), @@ -1511,6 +1509,29 @@ typedef struct KernelLightDistribution { } KernelLightDistribution; static_assert_align(KernelLightDistribution, 16); +typedef struct KernelParticle { + int index; + float age; + float lifetime; + float size; + float4 rotation; + /* Only xyz are used of the following. float4 instead of float3 are used + * to ensure consistent padding/alignment across devices. */ + float4 location; + float4 velocity; + float4 angular_velocity; +} KernelParticle; +static_assert_align(KernelParticle, 16); + +typedef struct KernelShader { + float constant_emission[3]; + float pad1; + int flags; + int pass_id; + int pad2, pad3; +} KernelShader; +static_assert_align(KernelShader, 16); + /* Declarations required for split kernel */ /* Macro for queues */ diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index 058e7dccafd..88360e5f1ae 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -104,7 +104,7 @@ ccl_device float kernel_volume_channel_get(float3 value, int channel) ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, ccl_addr_space VolumeStack *stack) { for(int i = 0; stack[i].shader != SHADER_NONE; i++) { - int shader_flag = kernel_tex_fetch(__shader_flag, (stack[i].shader & SHADER_MASK)*SHADER_SIZE); + int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags; if(shader_flag & SD_HETEROGENEOUS_VOLUME) { return true; @@ -134,7 +134,7 @@ ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stac int method = -1; for(int i = 0; stack[i].shader != SHADER_NONE; i++) { - int shader_flag = kernel_tex_fetch(__shader_flag, (stack[i].shader & SHADER_MASK)*SHADER_SIZE); + int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags; if(shader_flag & SD_VOLUME_MIS) { return SD_VOLUME_MIS; diff --git a/intern/cycles/render/particles.cpp b/intern/cycles/render/particles.cpp index 3ee620c9d01..e4be3306d7e 100644 --- a/intern/cycles/render/particles.cpp +++ b/intern/cycles/render/particles.cpp @@ -62,14 +62,10 @@ void ParticleSystemManager::device_update_particles(Device *, DeviceScene *dscen for(size_t j = 0; j < scene->particle_systems.size(); j++) num_particles += scene->particle_systems[j]->particles.size(); - float4 *particles = dscene->particles.alloc(PARTICLE_SIZE*num_particles); + KernelParticle *kparticles = dscene->particles.alloc(num_particles); /* dummy particle */ - particles[0] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - particles[1] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - particles[2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - particles[3] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - particles[4] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + memset(kparticles, 0, sizeof(KernelParticle)); int i = 1; for(size_t j = 0; j < scene->particle_systems.size(); j++) { @@ -78,13 +74,15 @@ void ParticleSystemManager::device_update_particles(Device *, DeviceScene *dscen for(size_t k = 0; k < psys->particles.size(); k++) { /* pack in texture */ Particle& pa = psys->particles[k]; - int offset = i*PARTICLE_SIZE; - particles[offset] = make_float4(__uint_as_float(pa.index), pa.age, pa.lifetime, pa.size); - particles[offset+1] = pa.rotation; - particles[offset+2] = make_float4(pa.location.x, pa.location.y, pa.location.z, pa.velocity.x); - particles[offset+3] = make_float4(pa.velocity.y, pa.velocity.z, pa.angular_velocity.x, pa.angular_velocity.y); - particles[offset+4] = make_float4(pa.angular_velocity.z, 0.0f, 0.0f, 0.0f); + kparticles[i].index = pa.index; + kparticles[i].age = pa.age; + kparticles[i].lifetime = pa.lifetime; + kparticles[i].size = pa.size; + kparticles[i].rotation = pa.rotation; + kparticles[i].location = float3_to_float4(pa.location); + kparticles[i].velocity = float3_to_float4(pa.velocity); + kparticles[i].angular_velocity = float3_to_float4(pa.angular_velocity); i++; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index aca28fc32fb..f5b8e2fd6a6 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -71,7 +71,7 @@ DeviceScene::DeviceScene(Device *device) light_background_conditional_cdf(device, "__light_background_conditional_cdf", MEM_TEXTURE), particles(device, "__particles", MEM_TEXTURE), svm_nodes(device, "__svm_nodes", MEM_TEXTURE), - shader_flag(device, "__shader_flag", MEM_TEXTURE), + shaders(device, "__shaders", MEM_TEXTURE), object_flag(device, "__object_flag", MEM_TEXTURE), lookup_table(device, "__lookup_table", MEM_TEXTURE), sobol_directions(device, "__sobol_directions", MEM_TEXTURE) diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 3f089b9138f..316ffeb1092 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -102,11 +102,11 @@ public: device_vector light_background_conditional_cdf; /* particles */ - device_vector particles; + device_vector particles; /* shaders */ device_vector svm_nodes; - device_vector shader_flag; + device_vector shaders; device_vector object_flag; /* lookup tables */ diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 578c61a3e79..ec52c51e337 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -432,14 +432,12 @@ void ShaderManager::device_update_common(Device *device, Scene *scene, Progress& /*progress*/) { - dscene->shader_flag.free(); + dscene->shaders.free(); if(scene->shaders.size() == 0) return; - uint shader_flag_size = scene->shaders.size()*SHADER_SIZE; - uint *shader_flag = dscene->shader_flag.alloc(shader_flag_size); - uint i = 0; + KernelShader *kshader = dscene->shaders.alloc(scene->shaders.size()); bool has_volumes = false; bool has_transparent_shadow = false; @@ -487,16 +485,17 @@ void ShaderManager::device_update_common(Device *device, flag |= SD_HAS_CONSTANT_EMISSION; /* regular shader */ - shader_flag[i++] = flag; - shader_flag[i++] = shader->pass_id; - shader_flag[i++] = __float_as_int(constant_emission.x); - shader_flag[i++] = __float_as_int(constant_emission.y); - shader_flag[i++] = __float_as_int(constant_emission.z); + kshader->flags = flag; + kshader->pass_id = shader->pass_id; + kshader->constant_emission[0] = constant_emission.x; + kshader->constant_emission[1] = constant_emission.y; + kshader->constant_emission[2] = constant_emission.z; + kshader++; has_transparent_shadow |= (flag & SD_HAS_TRANSPARENT_SHADOW) != 0; } - dscene->shader_flag.copy_to_device(); + dscene->shaders.copy_to_device(); /* lookup tables */ KernelTables *ktables = &dscene->data.tables; @@ -525,7 +524,7 @@ void ShaderManager::device_free_common(Device *, DeviceScene *dscene, Scene *sce { scene->lookup_tables->remove_table(&beckmann_table_offset); - dscene->shader_flag.free(); + dscene->shaders.free(); } void ShaderManager::add_default(Scene *scene) From cd15d87bfcb4aafb0d4f13dcc902a135f472c9df Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 7 Mar 2018 23:52:26 +0100 Subject: [PATCH 08/21] Code refactor: avoid motion transform copy, remove unused curved code. The purpose of the previous code refactoring is to make the code more readable, but combined with this change benchmarks also render about 2-3% faster with an NVIDIA Titan Xp. --- intern/cycles/kernel/geom/geom_object.h | 4 +- intern/cycles/kernel/kernel_camera.h | 42 +++++------------- intern/cycles/util/util_transform.h | 59 +++++++++++++------------ 3 files changed, 44 insertions(+), 61 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 0b410f448c8..3fbceded261 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -96,10 +96,10 @@ ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int #ifdef __OBJECT_MOTION__ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int object, float time) { - MotionTransform motion = kernel_tex_fetch(__objects, object).tfm; + const ccl_global MotionTransform *motion = &kernel_tex_fetch(__objects, object).tfm; Transform tfm; - transform_motion_interpolate(&tfm, &motion, time); + transform_motion_interpolate(&tfm, motion, time); return tfm; } diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 96cdb04d955..66ed9f5fc0f 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -92,16 +92,10 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo #ifdef __CAMERA_MOTION__ if(kernel_data.cam.have_motion) { -# ifdef __KERNEL_OPENCL__ - const MotionTransform tfm = kernel_data.cam.motion; - transform_motion_interpolate(&cameratoworld, - &tfm, - ray->time); -# else - transform_motion_interpolate(&cameratoworld, - &kernel_data.cam.motion, - ray->time); -# endif + ccl_constant MotionTransform *motion = &kernel_data.cam.motion; + transform_motion_interpolate_constant(&cameratoworld, + motion, + ray->time); } #endif @@ -204,16 +198,10 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl #ifdef __CAMERA_MOTION__ if(kernel_data.cam.have_motion) { -# ifdef __KERNEL_OPENCL__ - const MotionTransform tfm = kernel_data.cam.motion; - transform_motion_interpolate(&cameratoworld, - &tfm, - ray->time); -# else - transform_motion_interpolate(&cameratoworld, - &kernel_data.cam.motion, - ray->time); -# endif + ccl_constant MotionTransform *motion = &kernel_data.cam.motion; + transform_motion_interpolate_constant(&cameratoworld, + motion, + ray->time); } #endif @@ -282,16 +270,10 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, #ifdef __CAMERA_MOTION__ if(cam->have_motion) { -# ifdef __KERNEL_OPENCL__ - const MotionTransform tfm = cam->motion; - transform_motion_interpolate(&cameratoworld, - &tfm, - ray->time); -# else - transform_motion_interpolate(&cameratoworld, - &cam->motion, - ray->time); -# endif + ccl_constant MotionTransform *motion = &cam->motion; + transform_motion_interpolate_constant(&cameratoworld, + motion, + ray->time); } #endif diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index ac0804a7227..1efe001f6a8 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -455,40 +455,16 @@ ccl_device_inline void transform_compose(Transform *tfm, const Transform *decomp tfm->w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); } -/* Disabled for now, need arc-length parametrization for constant speed motion. - * #define CURVED_MOTION_INTERPOLATE */ - -ccl_device void transform_motion_interpolate(Transform *tfm, const MotionTransform *motion, float t) +ccl_device void transform_motion_interpolate(Transform *tfm, const ccl_global MotionTransform *motion, float t) { - /* possible optimization: is it worth it adding a check to skip scaling? - * it's probably quite uncommon to have scaling objects. or can we skip - * just shearing perhaps? */ Transform decomp; -#ifdef CURVED_MOTION_INTERPOLATE - /* 3 point bezier curve interpolation for position */ - float3 Ppre = float4_to_float3(motion->pre.y); - float3 Pmid = float4_to_float3(motion->mid.y); - float3 Ppost = float4_to_float3(motion->post.y); - - float3 Pcontrol = 2.0f*Pmid - 0.5f*(Ppre + Ppost); - float3 P = Ppre*t*t + Pcontrol*2.0f*t*(1.0f - t) + Ppost*(1.0f - t)*(1.0f - t); - - decomp.y.x = P.x; - decomp.y.y = P.y; - decomp.y.z = P.z; -#endif - /* linear interpolation for rotation and scale */ if(t < 0.5f) { t *= 2.0f; decomp.x = quat_interpolate(motion->pre.x, motion->mid.x, t); -#ifdef CURVED_MOTION_INTERPOLATE - decomp.y.w = (1.0f - t)*motion->pre.y.w + t*motion->mid.y.w; -#else decomp.y = (1.0f - t)*motion->pre.y + t*motion->mid.y; -#endif decomp.z = (1.0f - t)*motion->pre.z + t*motion->mid.z; decomp.w = (1.0f - t)*motion->pre.w + t*motion->mid.w; } @@ -496,11 +472,36 @@ ccl_device void transform_motion_interpolate(Transform *tfm, const MotionTransfo t = (t - 0.5f)*2.0f; decomp.x = quat_interpolate(motion->mid.x, motion->post.x, t); -#ifdef CURVED_MOTION_INTERPOLATE - decomp.y.w = (1.0f - t)*motion->mid.y.w + t*motion->post.y.w; -#else decomp.y = (1.0f - t)*motion->mid.y + t*motion->post.y; -#endif + decomp.z = (1.0f - t)*motion->mid.z + t*motion->post.z; + decomp.w = (1.0f - t)*motion->mid.w + t*motion->post.w; + } + + /* compose rotation, translation, scale into matrix */ + transform_compose(tfm, &decomp); +} + +ccl_device void transform_motion_interpolate_constant(Transform *tfm, ccl_constant MotionTransform *motion, float t) +{ + /* possible optimization: is it worth it adding a check to skip scaling? + * it's probably quite uncommon to have scaling objects. or can we skip + * just shearing perhaps? */ + Transform decomp; + + /* linear interpolation for rotation and scale */ + if(t < 0.5f) { + t *= 2.0f; + + decomp.x = quat_interpolate(motion->pre.x, motion->mid.x, t); + decomp.y = (1.0f - t)*motion->pre.y + t*motion->mid.y; + decomp.z = (1.0f - t)*motion->pre.z + t*motion->mid.z; + decomp.w = (1.0f - t)*motion->pre.w + t*motion->mid.w; + } + else { + t = (t - 0.5f)*2.0f; + + decomp.x = quat_interpolate(motion->mid.x, motion->post.x, t); + decomp.y = (1.0f - t)*motion->mid.y + t*motion->post.y; decomp.z = (1.0f - t)*motion->mid.z + t*motion->post.z; decomp.w = (1.0f - t)*motion->mid.w + t*motion->post.w; } From 516e82a90012da3911518103829158215d94215f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 8 Mar 2018 05:33:55 +0100 Subject: [PATCH 09/21] Code refactor: add ProjectionTransform separate from regular Transform. This is in preparation of making Transform affine only. --- intern/cycles/blender/blender_object_cull.cpp | 2 +- intern/cycles/kernel/CMakeLists.txt | 1 + intern/cycles/kernel/geom/geom_primitive.h | 12 +- intern/cycles/kernel/kernel_camera.h | 12 +- intern/cycles/kernel/kernel_math.h | 1 + intern/cycles/kernel/kernel_types.h | 21 +-- intern/cycles/kernel/osl/osl_services.cpp | 89 ++++----- intern/cycles/render/camera.cpp | 57 +++--- intern/cycles/render/camera.h | 17 +- intern/cycles/util/CMakeLists.txt | 1 + intern/cycles/util/util_projection.h | 176 ++++++++++++++++++ intern/cycles/util/util_transform.h | 35 ---- 12 files changed, 275 insertions(+), 149 deletions(-) create mode 100644 intern/cycles/util/util_projection.h diff --git a/intern/cycles/blender/blender_object_cull.cpp b/intern/cycles/blender/blender_object_cull.cpp index 1d747de647a..bdf7dc469b2 100644 --- a/intern/cycles/blender/blender_object_cull.cpp +++ b/intern/cycles/blender/blender_object_cull.cpp @@ -96,7 +96,7 @@ bool BlenderObjectCulling::test(Scene *scene, BL::Object& b_ob, Transform& tfm) bool BlenderObjectCulling::test_camera(Scene *scene, float3 bb[8]) { Camera *cam = scene->camera; - Transform& worldtondc = cam->worldtondc; + const ProjectionTransform& worldtondc = cam->worldtondc; float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX), bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX); bool all_behind = true; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 50ea03a1f8f..9b7f4e00084 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -254,6 +254,7 @@ set(SRC_UTIL_HEADERS ../util/util_math_int3.h ../util/util_math_int4.h ../util/util_math_matrix.h + ../util/util_projection.h ../util/util_rect.h ../util/util_static_assert.h ../util/util_transform.h diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index 60a1e483b84..6a8ea793ff0 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -204,14 +204,14 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * /* camera motion, for perspective/orthographic motion.pre/post will be a * world-to-raster matrix, for panorama it's world-to-camera */ if(kernel_data.cam.type != CAMERA_PANORAMA) { - tfm = kernel_data.cam.worldtoraster; - motion_center = transform_perspective(&tfm, center); + ProjectionTransform projection = kernel_data.cam.worldtoraster; + motion_center = transform_perspective(&projection, center); - tfm = kernel_data.cam.motion.pre; - motion_pre = transform_perspective(&tfm, motion_pre); + projection = kernel_data.cam.perspective_motion.pre; + motion_pre = transform_perspective(&projection, motion_pre); - tfm = kernel_data.cam.motion.post; - motion_post = transform_perspective(&tfm, motion_post); + projection = kernel_data.cam.perspective_motion.post; + motion_post = transform_perspective(&projection, motion_post); } else { tfm = kernel_data.cam.worldtocamera; diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 66ed9f5fc0f..5b102eabb93 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -42,7 +42,7 @@ ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, float u ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, float raster_y, float lens_u, float lens_v, ccl_addr_space Ray *ray) { /* create ray form raster position */ - Transform rastertocamera = kernel_data.cam.rastertocamera; + ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; float3 raster = make_float3(raster_x, raster_y, 0.0f); float3 Pcamera = transform_perspective(&rastertocamera, raster); @@ -54,13 +54,13 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo * interpolated field of view. */ if(ray->time < 0.5f) { - Transform rastertocamera_pre = kernel_data.cam.perspective_motion.pre; + ProjectionTransform rastertocamera_pre = kernel_data.cam.perspective_motion.pre; float3 Pcamera_pre = transform_perspective(&rastertocamera_pre, raster); Pcamera = interp(Pcamera_pre, Pcamera, ray->time * 2.0f); } else { - Transform rastertocamera_post = kernel_data.cam.perspective_motion.post; + ProjectionTransform rastertocamera_post = kernel_data.cam.perspective_motion.post; float3 Pcamera_post = transform_perspective(&rastertocamera_post, raster); Pcamera = interp(Pcamera, Pcamera_post, (ray->time - 0.5f) * 2.0f); @@ -169,7 +169,7 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, float raster_y, float lens_u, float lens_v, ccl_addr_space Ray *ray) { /* create ray form raster position */ - Transform rastertocamera = kernel_data.cam.rastertocamera; + ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); float3 P; @@ -231,7 +231,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, float lens_u, float lens_v, ccl_addr_space Ray *ray) { - Transform rastertocamera = cam->rastertocamera; + ProjectionTransform rastertocamera = cam->rastertocamera; float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); /* create ray form raster position */ @@ -442,7 +442,7 @@ ccl_device_inline float3 camera_world_to_ndc(KernelGlobals *kg, ShaderData *sd, if(sd->object == PRIM_NONE && kernel_data.cam.type == CAMERA_PERSPECTIVE) P += camera_position(kg); - Transform tfm = kernel_data.cam.worldtondc; + ProjectionTransform tfm = kernel_data.cam.worldtondc; return transform_perspective(&tfm, P); } else { diff --git a/intern/cycles/kernel/kernel_math.h b/intern/cycles/kernel/kernel_math.h index bd0e23b7705..96391db7649 100644 --- a/intern/cycles/kernel/kernel_math.h +++ b/intern/cycles/kernel/kernel_math.h @@ -21,6 +21,7 @@ #include "util/util_math.h" #include "util/util_math_fast.h" #include "util/util_math_intersect.h" +#include "util/util_projection.h" #include "util/util_texture.h" #include "util/util_transform.h" diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 2cab63cdc6a..87faa0d0e53 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1159,7 +1159,7 @@ typedef struct KernelCamera { /* matrices */ Transform cameratoworld; - Transform rastertocamera; + ProjectionTransform rastertocamera; /* differentials */ float4 dx; @@ -1193,21 +1193,18 @@ typedef struct KernelCamera { int is_inside_volume; /* more matrices */ - Transform screentoworld; - Transform rastertoworld; - /* work around cuda sm 2.0 crash, this seems to - * cross some limit in combination with motion - * Transform ndctoworld; */ - Transform worldtoscreen; - Transform worldtoraster; - Transform worldtondc; + ProjectionTransform screentoworld; + ProjectionTransform rastertoworld; + ProjectionTransform ndctoworld; + ProjectionTransform worldtoscreen; + ProjectionTransform worldtoraster; + ProjectionTransform worldtondc; Transform worldtocamera; MotionTransform motion; - /* Denotes changes in the projective matrix, namely in rastertocamera. - * Used for camera zoom motion blur, - */ + /* Stores changes in the projeciton matrix. Use for camera zoom motion + * blur and motion pass output for perspective camera. */ PerspectiveMotionTransform perspective_motion; int shutter_table_offset; diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index ae4c521659c..dec56c6665b 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -62,11 +62,18 @@ CCL_NAMESPACE_BEGIN /* RenderServices implementation */ -#define COPY_MATRIX44(m1, m2) { \ - CHECK_TYPE(m1, OSL::Matrix44*); \ - CHECK_TYPE(m2, Transform*); \ - memcpy(m1, m2, sizeof(*m2)); \ -} (void)0 +static void copy_matrix(OSL::Matrix44& m, const Transform& tfm) +{ + // TODO: remember when making affine + Transform t = transform_transpose(tfm); + memcpy(&m, &t, sizeof(m)); +} + +static void copy_matrix(OSL::Matrix44& m, const ProjectionTransform& tfm) +{ + ProjectionTransform t = projection_transpose(tfm); + memcpy(&m, &t, sizeof(m)); +} /* static ustrings */ ustring OSLRenderServices::u_distance("distance"); @@ -167,14 +174,12 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result #else Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); #endif - tfm = transform_transpose(tfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, tfm); return true; } else if(sd->type == PRIMITIVE_LAMP) { - Transform tfm = transform_transpose(sd->ob_tfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, sd->ob_tfm); return true; } @@ -203,14 +208,12 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 #else Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); #endif - itfm = transform_transpose(itfm); - COPY_MATRIX44(&result, &itfm); + copy_matrix(result, itfm); return true; } else if(sd->type == PRIMITIVE_LAMP) { - Transform tfm = transform_transpose(sd->ob_itfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, sd->ob_itfm); return true; } @@ -224,23 +227,19 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result KernelGlobals *kg = kernel_globals; if(from == u_ndc) { - Transform tfm = transform_transpose(transform_quick_inverse(kernel_data.cam.worldtondc)); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.ndctoworld); return true; } else if(from == u_raster) { - Transform tfm = transform_transpose(kernel_data.cam.rastertoworld); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.rastertoworld); return true; } else if(from == u_screen) { - Transform tfm = transform_transpose(kernel_data.cam.screentoworld); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.screentoworld); return true; } else if(from == u_camera) { - Transform tfm = transform_transpose(kernel_data.cam.cameratoworld); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.cameratoworld); return true; } else if(from == u_world) { @@ -256,23 +255,19 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 KernelGlobals *kg = kernel_globals; if(to == u_ndc) { - Transform tfm = transform_transpose(kernel_data.cam.worldtondc); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtondc); return true; } else if(to == u_raster) { - Transform tfm = transform_transpose(kernel_data.cam.worldtoraster); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtoraster); return true; } else if(to == u_screen) { - Transform tfm = transform_transpose(kernel_data.cam.worldtoscreen); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtoscreen); return true; } else if(to == u_camera) { - Transform tfm = transform_transpose(kernel_data.cam.worldtocamera); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtocamera); return true; } else if(to == u_world) { @@ -298,14 +293,12 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result KernelGlobals *kg = sd->osl_globals; Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); #endif - tfm = transform_transpose(tfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, tfm); return true; } else if(sd->type == PRIMITIVE_LAMP) { - Transform tfm = transform_transpose(sd->ob_tfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, sd->ob_tfm); return true; } @@ -329,14 +322,12 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 KernelGlobals *kg = sd->osl_globals; Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); #endif - tfm = transform_transpose(tfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, tfm); return true; } else if(sd->type == PRIMITIVE_LAMP) { - Transform tfm = transform_transpose(sd->ob_itfm); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, sd->ob_itfm); return true; } @@ -350,23 +341,19 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result KernelGlobals *kg = kernel_globals; if(from == u_ndc) { - Transform tfm = transform_transpose(transform_quick_inverse(kernel_data.cam.worldtondc)); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.ndctoworld); return true; } else if(from == u_raster) { - Transform tfm = transform_transpose(kernel_data.cam.rastertoworld); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.rastertoworld); return true; } else if(from == u_screen) { - Transform tfm = transform_transpose(kernel_data.cam.screentoworld); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.screentoworld); return true; } else if(from == u_camera) { - Transform tfm = transform_transpose(kernel_data.cam.cameratoworld); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.cameratoworld); return true; } @@ -378,23 +365,19 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 KernelGlobals *kg = kernel_globals; if(to == u_ndc) { - Transform tfm = transform_transpose(kernel_data.cam.worldtondc); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtondc); return true; } else if(to == u_raster) { - Transform tfm = transform_transpose(kernel_data.cam.worldtoraster); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtoraster); return true; } else if(to == u_screen) { - Transform tfm = transform_transpose(kernel_data.cam.worldtoscreen); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtoscreen); return true; } else if(to == u_camera) { - Transform tfm = transform_transpose(kernel_data.cam.worldtocamera); - COPY_MATRIX44(&result, &tfm); + copy_matrix(result, kernel_data.cam.worldtocamera); return true; } diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index a2fda12ec85..e6585b2de55 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -163,12 +163,12 @@ Camera::Camera() compute_auto_viewplane(); - screentoworld = transform_identity(); - rastertoworld = transform_identity(); - ndctoworld = transform_identity(); - rastertocamera = transform_identity(); + screentoworld = projection_identity(); + rastertoworld = projection_identity(); + ndctoworld = projection_identity(); + rastertocamera = projection_identity(); cameratoworld = transform_identity(); - worldtoraster = transform_identity(); + worldtoraster = projection_identity(); dx = make_float3(0.0f, 0.0f, 0.0f); dy = make_float3(0.0f, 0.0f, 0.0f); @@ -241,18 +241,18 @@ void Camera::update(Scene *scene) Transform full_rastertoscreen = transform_inverse(full_screentoraster); /* screen to camera */ - Transform cameratoscreen; + ProjectionTransform cameratoscreen; if(type == CAMERA_PERSPECTIVE) - cameratoscreen = transform_perspective(fov, nearclip, farclip); + cameratoscreen = projection_perspective(fov, nearclip, farclip); else if(type == CAMERA_ORTHOGRAPHIC) - cameratoscreen = transform_orthographic(nearclip, farclip); + cameratoscreen = projection_orthographic(nearclip, farclip); else - cameratoscreen = transform_identity(); + cameratoscreen = projection_identity(); - Transform screentocamera = transform_inverse(cameratoscreen); + ProjectionTransform screentocamera = projection_inverse(cameratoscreen); rastertocamera = screentocamera * rastertoscreen; - Transform full_rastertocamera = screentocamera * full_rastertoscreen; + ProjectionTransform full_rastertocamera = screentocamera * full_rastertoscreen; cameratoraster = screentoraster * cameratoscreen; cameratoworld = matrix; @@ -270,10 +270,10 @@ void Camera::update(Scene *scene) /* differentials */ if(type == CAMERA_ORTHOGRAPHIC) { - dx = transform_direction(&rastertocamera, make_float3(1, 0, 0)); - dy = transform_direction(&rastertocamera, make_float3(0, 1, 0)); - full_dx = transform_direction(&full_rastertocamera, make_float3(1, 0, 0)); - full_dy = transform_direction(&full_rastertocamera, make_float3(0, 1, 0)); + dx = transform_perspective_direction(&rastertocamera, make_float3(1, 0, 0)); + dy = transform_perspective_direction(&rastertocamera, make_float3(0, 1, 0)); + full_dx = transform_perspective_direction(&full_rastertocamera, make_float3(1, 0, 0)); + full_dy = transform_perspective_direction(&full_rastertocamera, make_float3(0, 1, 0)); } else if(type == CAMERA_PERSPECTIVE) { dx = transform_perspective(&rastertocamera, make_float3(1, 0, 0)) - @@ -307,14 +307,14 @@ void Camera::update(Scene *scene) /* TODO(sergey): Move to an utility function and de-duplicate with * calculation above. */ - Transform screentocamera_pre = - transform_inverse(transform_perspective(fov_pre, - nearclip, - farclip)); - Transform screentocamera_post = - transform_inverse(transform_perspective(fov_post, - nearclip, - farclip)); + ProjectionTransform screentocamera_pre = + projection_inverse(projection_perspective(fov_pre, + nearclip, + farclip)); + ProjectionTransform screentocamera_post = + projection_inverse(projection_perspective(fov_post, + nearclip, + farclip)); perspective_motion.pre = screentocamera_pre * rastertoscreen; perspective_motion.post = screentocamera_post * rastertoscreen; } @@ -331,6 +331,7 @@ void Camera::update(Scene *scene) kcam->worldtoscreen = worldtoscreen; kcam->worldtoraster = worldtoraster; kcam->worldtondc = worldtondc; + kcam->ndctoworld = ndctoworld; /* camera motion */ kcam->have_motion = 0; @@ -350,12 +351,12 @@ void Camera::update(Scene *scene) } else { if(use_motion) { - kcam->motion.pre = cameratoraster * transform_inverse(motion.pre); - kcam->motion.post = cameratoraster * transform_inverse(motion.post); + kcam->perspective_motion.pre = cameratoraster * transform_inverse(motion.pre); + kcam->perspective_motion.post = cameratoraster * transform_inverse(motion.post); } else { - kcam->motion.pre = worldtoraster; - kcam->motion.post = worldtoraster; + kcam->perspective_motion.pre = worldtoraster; + kcam->perspective_motion.post = worldtoraster; } } } @@ -606,7 +607,7 @@ float Camera::world_to_raster_size(float3 P) res = min(len(full_dx), len(full_dy)); if(offscreen_dicing_scale > 1.0f) { - float3 p = transform_perspective(&worldtocamera, P); + float3 p = transform_point(&worldtocamera, P); float3 v = transform_perspective(&rastertocamera, make_float3(width, height, 0.0f)); /* Create point clamped to frustum */ diff --git a/intern/cycles/render/camera.h b/intern/cycles/render/camera.h index 4ec0fe3bc6e..aa4e53a347b 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/render/camera.h @@ -22,6 +22,7 @@ #include "graph/node.h" #include "util/util_boundbox.h" +#include "util/util_projection.h" #include "util/util_transform.h" #include "util/util_types.h" @@ -146,18 +147,18 @@ public: PerspectiveMotionTransform perspective_motion; /* computed camera parameters */ - Transform screentoworld; - Transform rastertoworld; - Transform ndctoworld; + ProjectionTransform screentoworld; + ProjectionTransform rastertoworld; + ProjectionTransform ndctoworld; Transform cameratoworld; - Transform worldtoraster; - Transform worldtoscreen; - Transform worldtondc; + ProjectionTransform worldtoraster; + ProjectionTransform worldtoscreen; + ProjectionTransform worldtondc; Transform worldtocamera; - Transform rastertocamera; - Transform cameratoraster; + ProjectionTransform rastertocamera; + ProjectionTransform cameratoraster; float3 dx; float3 dy; diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 66c4f22a7e2..24043e2231b 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -67,6 +67,7 @@ set(SRC_HEADERS util_param.h util_path.h util_progress.h + util_projection.h util_queue.h util_rect.h util_set.h diff --git a/intern/cycles/util/util_projection.h b/intern/cycles/util/util_projection.h new file mode 100644 index 00000000000..0b8ff032120 --- /dev/null +++ b/intern/cycles/util/util_projection.h @@ -0,0 +1,176 @@ +/* + * Copyright 2011-2018 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 __UTIL_PROJECTION_H__ +#define __UTIL_PROJECTION_H__ + +#include "util/util_transform.h" + +CCL_NAMESPACE_BEGIN + +/* Data Types */ + +typedef struct ProjectionTransform { + float4 x, y, z, w; /* rows */ + +#ifndef __KERNEL_GPU__ + ProjectionTransform() + { + } + + explicit ProjectionTransform(const Transform& tfm) + : x(tfm.x), + y(tfm.y), + z(tfm.z), + w(tfm.w) + { + } +#endif +} ProjectionTransform; + +typedef struct PerspectiveMotionTransform { + ProjectionTransform pre; + ProjectionTransform post; +} PerspectiveMotionTransform; + +/* Functions */ + +ccl_device_inline float3 transform_perspective(const ProjectionTransform *t, const float3 a) +{ + float4 b = make_float4(a.x, a.y, a.z, 1.0f); + float3 c = make_float3(dot(t->x, b), dot(t->y, b), dot(t->z, b)); + float w = dot(t->w, b); + + return (w != 0.0f)? c/w: make_float3(0.0f, 0.0f, 0.0f); +} + +ccl_device_inline float3 transform_perspective_direction(const ProjectionTransform *t, const float3 a) +{ + float3 c = make_float3( + a.x*t->x.x + a.y*t->x.y + a.z*t->x.z, + a.x*t->y.x + a.y*t->y.y + a.z*t->y.z, + a.x*t->z.x + a.y*t->z.y + a.z*t->z.z); + + return c; +} + +#ifndef __KERNEL_GPU__ + +ccl_device_inline ProjectionTransform projection_transpose(const ProjectionTransform& a) +{ + ProjectionTransform t; + + t.x.x = a.x.x; t.x.y = a.y.x; t.x.z = a.z.x; t.x.w = a.w.x; + t.y.x = a.x.y; t.y.y = a.y.y; t.y.z = a.z.y; t.y.w = a.w.y; + t.z.x = a.x.z; t.z.y = a.y.z; t.z.z = a.z.z; t.z.w = a.w.z; + t.w.x = a.x.w; t.w.y = a.y.w; t.w.z = a.z.w; t.w.w = a.w.w; + + return t; +} + +ccl_device_inline ProjectionTransform projection_inverse(const ProjectionTransform& a) +{ + Transform t = {a.x, a.y, a.z, a.w}; + t = transform_inverse(t); + return ProjectionTransform(t); +} + +ccl_device_inline ProjectionTransform make_projection( + float a, float b, float c, float d, + float e, float f, float g, float h, + float i, float j, float k, float l, + float m, float n, float o, float p) +{ + ProjectionTransform t; + + t.x.x = a; t.x.y = b; t.x.z = c; t.x.w = d; + t.y.x = e; t.y.y = f; t.y.z = g; t.y.w = h; + t.z.x = i; t.z.y = j; t.z.z = k; t.z.w = l; + t.w.x = m; t.w.y = n; t.w.z = o; t.w.w = p; + + return t; +} +ccl_device_inline ProjectionTransform projection_identity() +{ + return make_projection( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); +} + +ccl_device_inline ProjectionTransform operator*(const ProjectionTransform& a, const ProjectionTransform& b) +{ + ProjectionTransform c = projection_transpose(b); + ProjectionTransform t; + + t.x = make_float4(dot(a.x, c.x), dot(a.x, c.y), dot(a.x, c.z), dot(a.x, c.w)); + t.y = make_float4(dot(a.y, c.x), dot(a.y, c.y), dot(a.y, c.z), dot(a.y, c.w)); + t.z = make_float4(dot(a.z, c.x), dot(a.z, c.y), dot(a.z, c.z), dot(a.z, c.w)); + t.w = make_float4(dot(a.w, c.x), dot(a.w, c.y), dot(a.w, c.z), dot(a.w, c.w)); + + return t; +} + +ccl_device_inline ProjectionTransform operator*(const ProjectionTransform& a, const Transform& b) +{ + return a * ProjectionTransform(b); +} + +ccl_device_inline ProjectionTransform operator*(const Transform& a, const ProjectionTransform& b) +{ + return ProjectionTransform(a) * b; +} + +ccl_device_inline void print_projection(const char *label, const ProjectionTransform& t) +{ + print_float4(label, t.x); + print_float4(label, t.y); + print_float4(label, t.z); + print_float4(label, t.w); + printf("\n"); +} + +ccl_device_inline ProjectionTransform projection_perspective(float fov, float n, float f) +{ + ProjectionTransform persp = make_projection( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, f / (f - n), -f*n / (f - n), + 0, 0, 1, 0); + + float inv_angle = 1.0f/tanf(0.5f*fov); + + Transform scale = transform_scale(inv_angle, inv_angle, 1); + + return scale * persp; +} + +ccl_device_inline ProjectionTransform projection_orthographic(float znear, float zfar) +{ + Transform t = + transform_scale(1.0f, 1.0f, 1.0f / (zfar-znear)) * + transform_translate(0.0f, 0.0f, -znear); + + return ProjectionTransform(t); +} + +#endif /* __KERNEL_GPU__ */ + +CCL_NAMESPACE_END + +#endif /* __UTIL_PROJECTION_H__ */ + diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index 1efe001f6a8..52022292d0b 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -47,22 +47,8 @@ typedef struct ccl_may_alias MotionTransform { Transform post; } MotionTransform; -typedef struct PerspectiveMotionTransform { - Transform pre; - Transform post; -} PerspectiveMotionTransform; - /* Functions */ -ccl_device_inline float3 transform_perspective(const Transform *t, const float3 a) -{ - float4 b = make_float4(a.x, a.y, a.z, 1.0f); - float3 c = make_float3(dot(t->x, b), dot(t->y, b), dot(t->z, b)); - float w = dot(t->w, b); - - return (w != 0.0f)? c/w: make_float3(0.0f, 0.0f, 0.0f); -} - ccl_device_inline float3 transform_point(const Transform *t, const float3 a) { /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ @@ -221,21 +207,6 @@ ccl_device_inline Transform transform_scale(float x, float y, float z) return transform_scale(make_float3(x, y, z)); } -ccl_device_inline Transform transform_perspective(float fov, float n, float f) -{ - Transform persp = make_transform( - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, f / (f - n), -f*n / (f - n), - 0, 0, 1, 0); - - float inv_angle = 1.0f/tanf(0.5f*fov); - - Transform scale = transform_scale(inv_angle, inv_angle, 1); - - return scale * persp; -} - ccl_device_inline Transform transform_rotate(float angle, float3 axis) { float s = sinf(angle); @@ -272,12 +243,6 @@ ccl_device_inline Transform transform_euler(float3 euler) transform_rotate(euler.x, make_float3(1.0f, 0.0f, 0.0f)); } -ccl_device_inline Transform transform_orthographic(float znear, float zfar) -{ - return transform_scale(1.0f, 1.0f, 1.0f / (zfar-znear)) * - transform_translate(0.0f, 0.0f, -znear); -} - ccl_device_inline Transform transform_identity() { return transform_scale(1.0f, 1.0f, 1.0f); From 623141f339d5066ed6b96ad70ab45fb294e3e612 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 8 Mar 2018 06:19:17 +0100 Subject: [PATCH 10/21] Code refactor: add DecomposedTransform. This is in preparation of making Transform affine only, and also gives us a little extra type safety so we don't accidentally treat it as a regular 4x4 matrix. --- intern/cycles/kernel/geom/geom_object.h | 2 +- intern/cycles/kernel/geom/geom_primitive.h | 4 +-- intern/cycles/kernel/kernel_camera.h | 6 ++-- intern/cycles/kernel/kernel_types.h | 7 +++-- intern/cycles/render/camera.cpp | 8 ++--- intern/cycles/render/object.cpp | 4 +-- intern/cycles/util/util_projection.h | 2 +- intern/cycles/util/util_transform.cpp | 4 +-- intern/cycles/util/util_transform.h | 35 +++++++++++++++------- 9 files changed, 45 insertions(+), 27 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 3fbceded261..cd330308e07 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -96,7 +96,7 @@ ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int #ifdef __OBJECT_MOTION__ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int object, float time) { - const ccl_global MotionTransform *motion = &kernel_tex_fetch(__objects, object).tfm; + const ccl_global DecomposedMotionTransform *motion = &kernel_tex_fetch(__objects, object).tfm; Transform tfm; transform_motion_interpolate(&tfm, motion, time); diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index 6a8ea793ff0..4e84446bae0 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -220,13 +220,13 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * motion_center.x *= kernel_data.cam.width; motion_center.y *= kernel_data.cam.height; - tfm = kernel_data.cam.motion.pre; + tfm = kernel_data.cam.pass_motion.pre; motion_pre = normalize(transform_point(&tfm, motion_pre)); motion_pre = float2_to_float3(direction_to_panorama(&kernel_data.cam, motion_pre)); motion_pre.x *= kernel_data.cam.width; motion_pre.y *= kernel_data.cam.height; - tfm = kernel_data.cam.motion.post; + tfm = kernel_data.cam.pass_motion.post; motion_post = normalize(transform_point(&tfm, motion_post)); motion_post = float2_to_float3(direction_to_panorama(&kernel_data.cam, motion_post)); motion_post.x *= kernel_data.cam.width; diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 5b102eabb93..755c3472072 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -92,7 +92,7 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo #ifdef __CAMERA_MOTION__ if(kernel_data.cam.have_motion) { - ccl_constant MotionTransform *motion = &kernel_data.cam.motion; + ccl_constant DecomposedMotionTransform *motion = &kernel_data.cam.motion; transform_motion_interpolate_constant(&cameratoworld, motion, ray->time); @@ -198,7 +198,7 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl #ifdef __CAMERA_MOTION__ if(kernel_data.cam.have_motion) { - ccl_constant MotionTransform *motion = &kernel_data.cam.motion; + ccl_constant DecomposedMotionTransform *motion = &kernel_data.cam.motion; transform_motion_interpolate_constant(&cameratoworld, motion, ray->time); @@ -270,7 +270,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, #ifdef __CAMERA_MOTION__ if(cam->have_motion) { - ccl_constant MotionTransform *motion = &cam->motion; + ccl_constant DecomposedMotionTransform *motion = &cam->motion; transform_motion_interpolate_constant(&cameratoworld, motion, ray->time); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 87faa0d0e53..5934ff53f57 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1201,12 +1201,15 @@ typedef struct KernelCamera { ProjectionTransform worldtondc; Transform worldtocamera; - MotionTransform motion; + DecomposedMotionTransform motion; /* Stores changes in the projeciton matrix. Use for camera zoom motion * blur and motion pass output for perspective camera. */ PerspectiveMotionTransform perspective_motion; + /* Transforms for motion pass. */ + MotionTransform pass_motion; + int shutter_table_offset; /* Rolling shutter */ @@ -1430,7 +1433,7 @@ static_assert_align(KernelData, 16); /* Kernel data structures. */ typedef struct KernelObject { - MotionTransform tfm; + DecomposedMotionTransform tfm; float surface_area; float pass_id; diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index e6585b2de55..5e8c5db6c0e 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -341,12 +341,12 @@ void Camera::update(Scene *scene) /* TODO(sergey): Support perspective (zoom, fov) motion. */ if(type == CAMERA_PANORAMA) { if(use_motion) { - kcam->motion.pre = transform_inverse(motion.pre); - kcam->motion.post = transform_inverse(motion.post); + kcam->pass_motion.pre = transform_inverse(motion.pre); + kcam->pass_motion.post = transform_inverse(motion.post); } else { - kcam->motion.pre = kcam->worldtocamera; - kcam->motion.post = kcam->worldtocamera; + kcam->pass_motion.pre = kcam->worldtocamera; + kcam->pass_motion.post = kcam->worldtocamera; } } else { diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index c992469cb39..f632f7c4a53 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -97,7 +97,7 @@ void Object::compute_bounds(bool motion_blur) mtfm.post = tfm; } - MotionTransform decomp; + DecomposedMotionTransform decomp; transform_motion_decompose(&decomp, &mtfm, &tfm); bounds = BoundBox::empty; @@ -401,7 +401,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s else if(state->need_motion == Scene::MOTION_BLUR) { if(ob->use_motion) { /* decompose transformations for interpolation. */ - MotionTransform decomp; + DecomposedMotionTransform decomp; transform_motion_decompose(&decomp, &ob->motion, &ob->tfm); kobject.tfm = decomp; diff --git a/intern/cycles/util/util_projection.h b/intern/cycles/util/util_projection.h index 0b8ff032120..8073568046d 100644 --- a/intern/cycles/util/util_projection.h +++ b/intern/cycles/util/util_projection.h @@ -21,7 +21,7 @@ CCL_NAMESPACE_BEGIN -/* Data Types */ +/* 4x4 projection matrix, perspective or orthographic. */ typedef struct ProjectionTransform { float4 x, y, z, w; /* rows */ diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/util_transform.cpp index c1270545339..edb53cc06b7 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/util_transform.cpp @@ -202,7 +202,7 @@ float4 transform_to_quat(const Transform& tfm) return qt; } -static void transform_decompose(Transform *decomp, const Transform *tfm) +static void transform_decompose(DecomposedTransform *decomp, const Transform *tfm) { /* extract translation */ decomp->y = make_float4(tfm->x.w, tfm->y.w, tfm->z.w, 0.0f); @@ -247,7 +247,7 @@ static void transform_decompose(Transform *decomp, const Transform *tfm) decomp->w = make_float4(scale.y.z, scale.z.x, scale.z.y, scale.z.z); } -void transform_motion_decompose(MotionTransform *decomp, const MotionTransform *motion, const Transform *mid) +void transform_motion_decompose(DecomposedMotionTransform *decomp, const MotionTransform *motion, const Transform *mid) { transform_decompose(&decomp->pre, &motion->pre); transform_decompose(&decomp->mid, mid); diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index 52022292d0b..65166e325e8 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -37,16 +37,26 @@ typedef struct Transform { #endif } Transform; -/* transform decomposed in rotation/translation/scale. we use the same data - * structure as Transform, and tightly pack decomposition into it. first the - * rotation (4), then translation (3), then 3x3 scale matrix (9). */ - typedef struct ccl_may_alias MotionTransform { Transform pre; Transform mid; Transform post; } MotionTransform; +/* Transform decomposed in rotation/translation/scale. we use the same data + * structure as Transform, and tightly pack decomposition into it. first the + * rotation (4), then translation (3), then 3x3 scale matrix (9). */ + +typedef struct DecomposedTransform { + float4 x, y, z, w; +} DecomposedTransform; + +typedef struct ccl_may_alias DecomposedMotionTransform { + DecomposedTransform pre; + DecomposedTransform mid; + DecomposedTransform post; +} DecomposedMotionTransform; + /* Functions */ ccl_device_inline float3 transform_point(const Transform *t, const float3 a) @@ -384,7 +394,7 @@ ccl_device_inline Transform transform_quick_inverse(Transform M) return R; } -ccl_device_inline void transform_compose(Transform *tfm, const Transform *decomp) +ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransform *decomp) { /* rotation */ float q0, q1, q2, q3, qda, qdb, qdc, qaa, qab, qac, qbb, qbc, qcc; @@ -420,9 +430,9 @@ ccl_device_inline void transform_compose(Transform *tfm, const Transform *decomp tfm->w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); } -ccl_device void transform_motion_interpolate(Transform *tfm, const ccl_global MotionTransform *motion, float t) +ccl_device void transform_motion_interpolate(Transform *tfm, const ccl_global DecomposedMotionTransform *motion, float t) { - Transform decomp; + DecomposedTransform decomp; /* linear interpolation for rotation and scale */ if(t < 0.5f) { @@ -446,12 +456,12 @@ ccl_device void transform_motion_interpolate(Transform *tfm, const ccl_global Mo transform_compose(tfm, &decomp); } -ccl_device void transform_motion_interpolate_constant(Transform *tfm, ccl_constant MotionTransform *motion, float t) +ccl_device void transform_motion_interpolate_constant(Transform *tfm, ccl_constant DecomposedMotionTransform *motion, float t) { /* possible optimization: is it worth it adding a check to skip scaling? * it's probably quite uncommon to have scaling objects. or can we skip * just shearing perhaps? */ - Transform decomp; + DecomposedTransform decomp; /* linear interpolation for rotation and scale */ if(t < 0.5f) { @@ -479,13 +489,18 @@ ccl_device void transform_motion_interpolate_constant(Transform *tfm, ccl_consta class BoundBox2D; +ccl_device_inline bool operator==(const DecomposedTransform& A, const DecomposedTransform& B) +{ + return memcmp(&A, &B, sizeof(DecomposedTransform)) == 0; +} + ccl_device_inline bool operator==(const MotionTransform& A, const MotionTransform& B) { return (A.pre == B.pre && A.post == B.post); } float4 transform_to_quat(const Transform& tfm); -void transform_motion_decompose(MotionTransform *decomp, const MotionTransform *motion, const Transform *mid); +void transform_motion_decompose(DecomposedMotionTransform *decomp, const MotionTransform *motion, const Transform *mid); Transform transform_from_viewplane(BoundBox2D& viewplane); #endif From b66efbecf4780c65833f72ac8de5d18b5bca7e15 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 8 Mar 2018 06:48:14 +0100 Subject: [PATCH 11/21] Code refactor: make Transform always affine, dropping last row. This save a little memory and copying in the kernel by storing only a 4x3 matrix instead of a 4x4 matrix. We already did this in a few places, and those don't need to be special exceptions anymore now. --- intern/cycles/app/cycles_xml.cpp | 7 ++- intern/cycles/blender/blender_camera.cpp | 6 +- intern/cycles/blender/blender_util.h | 13 ++-- intern/cycles/graph/node_type.cpp | 2 +- intern/cycles/graph/node_xml.cpp | 23 ++++--- intern/cycles/kernel/bvh/bvh_nodes.h | 1 - intern/cycles/kernel/geom/geom_attribute.h | 1 - .../cycles/kernel/geom/geom_curve_intersect.h | 3 +- intern/cycles/kernel/geom/geom_object.h | 27 ++------ intern/cycles/kernel/kernel_textures.h | 2 +- intern/cycles/kernel/kernel_types.h | 8 +-- intern/cycles/kernel/osl/osl_services.cpp | 6 +- intern/cycles/kernel/svm/svm_mapping.h | 1 - intern/cycles/kernel/svm/svm_tex_coord.h | 3 - intern/cycles/kernel/svm/svm_voxel.h | 1 - intern/cycles/render/light.cpp | 13 ++-- intern/cycles/render/mesh.cpp | 6 +- intern/cycles/render/nodes.cpp | 17 ++--- intern/cycles/render/object.cpp | 15 ++--- intern/cycles/render/object.h | 2 +- intern/cycles/render/osl.cpp | 15 ++++- intern/cycles/render/scene.h | 2 +- intern/cycles/util/util_projection.h | 15 ++--- intern/cycles/util/util_transform.cpp | 26 ++++++-- intern/cycles/util/util_transform.h | 63 +++++++------------ 25 files changed, 120 insertions(+), 158 deletions(-) diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 21ae07e23b8..a46955322e3 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -40,6 +40,7 @@ #include "util/util_foreach.h" #include "util/util_path.h" +#include "util/util_projection.h" #include "util/util_transform.h" #include "util/util_xml.h" @@ -546,8 +547,10 @@ static void xml_read_transform(xml_node node, Transform& tfm) { if(node.attribute("matrix")) { vector matrix; - if(xml_read_float_array(matrix, node, "matrix") && matrix.size() == 16) - tfm = tfm * transform_transpose((*(Transform*)&matrix[0])); + if(xml_read_float_array(matrix, node, "matrix") && matrix.size() == 16) { + ProjectionTransform projection = *(ProjectionTransform*)&matrix[0]; + tfm = tfm * projection_to_transform(projection_transpose(projection)); + } } if(node.attribute("translate")) { diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 62e950e3bef..e5f885c0012 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -246,8 +246,7 @@ static Transform blender_camera_matrix(const Transform& tfm, result = tfm * make_transform(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); + 0.0f, 1.0f, 0.0f, 0.0f); } else { /* Make it so environment camera needs to be pointed in the direction @@ -257,8 +256,7 @@ static Transform blender_camera_matrix(const Transform& tfm, result = tfm * make_transform( 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); + -1.0f, 0.0f, 0.0f, 0.0f); } } else { diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 363e19f7a20..c4c45035b5a 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -247,14 +247,15 @@ static inline float *image_get_float_pixels_for_frame(BL::Image& image, static inline Transform get_transform(const BL::Array& array) { - Transform tfm; + ProjectionTransform projection; - /* we assume both types to be just 16 floats, and transpose because blender - * use column major matrix order while we use row major */ - memcpy(&tfm, &array, sizeof(float)*16); - tfm = transform_transpose(tfm); + /* We assume both types to be just 16 floats, and transpose because blender + * use column major matrix order while we use row major. */ + memcpy(&projection, &array, sizeof(float)*16); + projection = projection_transpose(projection); - return tfm; + /* Drop last row, matrix is assumed to be affine transform. */ + return projection_to_transform(projection); } static inline float2 get_float2(const BL::Array& array) diff --git a/intern/cycles/graph/node_type.cpp b/intern/cycles/graph/node_type.cpp index a3a8fa5f382..37aae211e93 100644 --- a/intern/cycles/graph/node_type.cpp +++ b/intern/cycles/graph/node_type.cpp @@ -77,7 +77,7 @@ size_t SocketType::max_size() void *SocketType::zero_default_value() { - static Transform zero_transform = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; + static Transform zero_transform = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; return &zero_transform; } diff --git a/intern/cycles/graph/node_xml.cpp b/intern/cycles/graph/node_xml.cpp index d26b3b2c2c8..f4599e22d40 100644 --- a/intern/cycles/graph/node_xml.cpp +++ b/intern/cycles/graph/node_xml.cpp @@ -196,7 +196,7 @@ void xml_read_node(XMLReader& reader, Node *node, xml_node xml_node) case SocketType::TRANSFORM: { array value; - xml_read_float_array<16>(value, attr); + xml_read_float_array<12>(value, attr); if(value.size() == 1) { node->set(socket, value[0]); } @@ -205,7 +205,7 @@ void xml_read_node(XMLReader& reader, Node *node, xml_node xml_node) case SocketType::TRANSFORM_ARRAY: { array value; - xml_read_float_array<16>(value, attr); + xml_read_float_array<12>(value, attr); node->set(socket, value); break; } @@ -400,12 +400,10 @@ xml_node xml_write_node(Node *node, xml_node xml_root) { Transform tfm = node->get_transform(socket); std::stringstream ss; - for(int i = 0; i < 4; i++) { - ss << string_printf("%g %g %g %g", (double)tfm[i][0], (double)tfm[i][1], (double)tfm[i][2], (double)tfm[i][3]); - if(i != 3) { - ss << " "; - } + for(int i = 0; i < 3; i++) { + ss << string_printf("%g %g %g %g ", (double)tfm[i][0], (double)tfm[i][1], (double)tfm[i][2], (double)tfm[i][3]); } + ss << string_printf("%g %g %g %g", 0.0, 0.0, 0.0, 1.0); attr = ss.str().c_str(); break; } @@ -416,11 +414,12 @@ xml_node xml_write_node(Node *node, xml_node xml_root) for(size_t j = 0; j < value.size(); j++) { const Transform& tfm = value[j]; - for(int i = 0; i < 4; i++) { - ss << string_printf("%g %g %g %g", (double)tfm[i][0], (double)tfm[i][1], (double)tfm[i][2], (double)tfm[i][3]); - if(j != value.size() - 1 || i != 3) { - ss << " "; - } + for(int i = 0; i < 3; i++) { + ss << string_printf("%g %g %g %g ", (double)tfm[i][0], (double)tfm[i][1], (double)tfm[i][2], (double)tfm[i][3]); + } + ss << string_printf("%g %g %g %g", 0.0, 0.0, 0.0, 1.0); + if(j != value.size() - 1) { + ss << " "; } } attr = ss.str().c_str(); diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/bvh_nodes.h index 6c33dad5426..060b3934a41 100644 --- a/intern/cycles/kernel/bvh/bvh_nodes.h +++ b/intern/cycles/kernel/bvh/bvh_nodes.h @@ -25,7 +25,6 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals *k space.x = kernel_tex_fetch(__bvh_nodes, child_addr+1); space.y = kernel_tex_fetch(__bvh_nodes, child_addr+2); space.z = kernel_tex_fetch(__bvh_nodes, child_addr+3); - space.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); return space; } diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h index 6e2ee3b62d7..42c053704d5 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/geom_attribute.h @@ -103,7 +103,6 @@ ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, const ShaderD tfm.x = kernel_tex_fetch(__attributes_float3, desc.offset + 0); tfm.y = kernel_tex_fetch(__attributes_float3, desc.offset + 1); tfm.z = kernel_tex_fetch(__attributes_float3, desc.offset + 2); - tfm.w = kernel_tex_fetch(__attributes_float3, desc.offset + 3); return tfm; } diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index faf3e3cdf2b..46c3f408f0b 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -170,8 +170,7 @@ ccl_device_forceinline bool cardinal_curve_intersect( htfm = make_transform( dir.z / d, 0, -dir.x /d, 0, -dir.x * dir.y /d, d, -dir.y * dir.z /d, 0, - dir.x, dir.y, dir.z, 0, - 0, 0, 0, 1); + dir.x, dir.y, dir.z, 0); float4 v00 = kernel_tex_fetch(__curves, prim); diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index cd330308e07..62bc46dc21b 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -28,12 +28,12 @@ CCL_NAMESPACE_BEGIN enum ObjectTransform { OBJECT_TRANSFORM = 0, - OBJECT_INVERSE_TRANSFORM = 4, + OBJECT_INVERSE_TRANSFORM = 1, }; enum ObjectVectorTransform { OBJECT_VECTOR_MOTION_PRE = 0, - OBJECT_VECTOR_MOTION_POST = 3 + OBJECT_VECTOR_MOTION_POST = 1 }; /* Object to world space transformation */ @@ -51,8 +51,6 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object tfm.y = kernel_tex_fetch(__objects, object).tfm.pre.y; tfm.z = kernel_tex_fetch(__objects, object).tfm.pre.z; } - tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); - return tfm; } @@ -60,20 +58,12 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse) { - Transform tfm; if(inverse) { - tfm.x = kernel_tex_fetch(__lights, lamp).itfm[0]; - tfm.y = kernel_tex_fetch(__lights, lamp).itfm[1]; - tfm.z = kernel_tex_fetch(__lights, lamp).itfm[2]; + return kernel_tex_fetch(__lights, lamp).itfm; } else { - tfm.x = kernel_tex_fetch(__lights, lamp).tfm[0]; - tfm.y = kernel_tex_fetch(__lights, lamp).tfm[1]; - tfm.z = kernel_tex_fetch(__lights, lamp).tfm[2]; + return kernel_tex_fetch(__lights, lamp).tfm; } - tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); - - return tfm; } /* Object to world space transformation for motion vectors */ @@ -81,14 +71,7 @@ ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bo ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int object, enum ObjectVectorTransform type) { int offset = object*OBJECT_VECTOR_SIZE + (int)type; - - Transform tfm; - tfm.x = kernel_tex_fetch(__objects_vector, offset + 0); - tfm.y = kernel_tex_fetch(__objects_vector, offset + 1); - tfm.z = kernel_tex_fetch(__objects_vector, offset + 2); - tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); - - return tfm; + return kernel_tex_fetch(__objects_vector, offset); } /* Motion blurred object transformations */ diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 892e0c84cba..6e07d88008e 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -32,7 +32,7 @@ KERNEL_TEX(float2, __prim_time) /* objects */ KERNEL_TEX(KernelObject, __objects) -KERNEL_TEX(float4, __objects_vector) +KERNEL_TEX(Transform, __objects_vector) /* triangles */ KERNEL_TEX(uint, __tri_shader) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 5934ff53f57..567f2a04028 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -35,10 +35,10 @@ CCL_NAMESPACE_BEGIN /* Constants */ -#define OBJECT_VECTOR_SIZE 6 +#define OBJECT_VECTOR_SIZE 2 #define FILTER_TABLE_SIZE 1024 #define RAMP_TABLE_SIZE 256 -#define SHUTTER_TABLE_SIZE 256 +#define SHUTTER_TABLE_SIZE 256 #define BSSRDF_MIN_RADIUS 1e-8f #define BSSRDF_MAX_HITS 4 @@ -1483,8 +1483,8 @@ typedef struct KernelLight { int samples; float max_bounces; float random; - float4 tfm[3]; - float4 itfm[3]; + Transform tfm; + Transform itfm; union { KernelSpotLight spot; KernelAreaLight area; diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index dec56c6665b..0c5e5e30e47 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -64,8 +64,7 @@ CCL_NAMESPACE_BEGIN static void copy_matrix(OSL::Matrix44& m, const Transform& tfm) { - // TODO: remember when making affine - Transform t = transform_transpose(tfm); + ProjectionTransform t = projection_transpose(ProjectionTransform(tfm)); memcpy(&m, &t, sizeof(m)); } @@ -553,8 +552,7 @@ static bool set_attribute_float3_3(float3 P[3], TypeDesc type, bool derivatives, static bool set_attribute_matrix(const Transform& tfm, TypeDesc type, void *val) { if(type == TypeDesc::TypeMatrix) { - Transform transpose = transform_transpose(tfm); - memcpy(val, &transpose, sizeof(Transform)); + copy_matrix(*(OSL::Matrix44*)val, tfm); return true; } diff --git a/intern/cycles/kernel/svm/svm_mapping.h b/intern/cycles/kernel/svm/svm_mapping.h index 0a890545af4..42a7ae9946f 100644 --- a/intern/cycles/kernel/svm/svm_mapping.h +++ b/intern/cycles/kernel/svm/svm_mapping.h @@ -26,7 +26,6 @@ ccl_device void svm_node_mapping(KernelGlobals *kg, ShaderData *sd, float *stack tfm.x = read_node_float(kg, offset); tfm.y = read_node_float(kg, offset); tfm.z = read_node_float(kg, offset); - tfm.w = read_node_float(kg, offset); float3 r = transform_point(&tfm, v); stack_store_float3(stack, out_offset, r); diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index c94327401f5..6ff39e5f587 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -42,7 +42,6 @@ ccl_device void svm_node_tex_coord(KernelGlobals *kg, tfm.x = read_node_float(kg, offset); tfm.y = read_node_float(kg, offset); tfm.z = read_node_float(kg, offset); - tfm.w = read_node_float(kg, offset); data = transform_point(&tfm, data); } break; @@ -123,7 +122,6 @@ ccl_device void svm_node_tex_coord_bump_dx(KernelGlobals *kg, tfm.x = read_node_float(kg, offset); tfm.y = read_node_float(kg, offset); tfm.z = read_node_float(kg, offset); - tfm.w = read_node_float(kg, offset); data = transform_point(&tfm, data); } break; @@ -207,7 +205,6 @@ ccl_device void svm_node_tex_coord_bump_dy(KernelGlobals *kg, tfm.x = read_node_float(kg, offset); tfm.y = read_node_float(kg, offset); tfm.z = read_node_float(kg, offset); - tfm.w = read_node_float(kg, offset); data = transform_point(&tfm, data); } break; diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h index d967516a5c9..43b433683e0 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -39,7 +39,6 @@ ccl_device void svm_node_tex_voxel(KernelGlobals *kg, tfm.x = read_node_float(kg, offset); tfm.y = read_node_float(kg, offset); tfm.z = read_node_float(kg, offset); - tfm.w = read_node_float(kg, offset); co = transform_point(&tfm, co); } diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index a3f67361f6e..8dec7e4ea64 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -785,11 +785,8 @@ void LightManager::device_update_points(Device *, klights[light_index].max_bounces = max_bounces; klights[light_index].random = random; - - Transform tfm = light->tfm; - Transform itfm = transform_inverse(tfm); - memcpy(&klights[light_index].tfm, &tfm, sizeof(float4)*3); - memcpy(&klights[light_index].itfm, &itfm, sizeof(float4)*3); + klights[light_index].tfm = light->tfm; + klights[light_index].itfm = transform_inverse(light->tfm); light_index++; } @@ -825,10 +822,8 @@ void LightManager::device_update_points(Device *, klights[light_index].area.dir[0] = dir.x; klights[light_index].area.dir[1] = dir.y; klights[light_index].area.dir[2] = dir.z; - Transform tfm = light->tfm; - Transform itfm = transform_inverse(tfm); - memcpy(&klights[light_index].tfm, &tfm, sizeof(float4)*3); - memcpy(&klights[light_index].itfm, &itfm, sizeof(float4)*3); + klights[light_index].tfm = light->tfm; + klights[light_index].itfm = transform_inverse(light->tfm); light_index++; } diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 47d24970949..71cbf5c24b6 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -1445,11 +1445,11 @@ static void update_attribute_element_offset(Mesh *mesh, Transform *tfm = mattr->data_transform(); offset = attr_float3_offset; - assert(attr_float3.size() >= offset + size * 4); - for(size_t k = 0; k < size*4; k++) { + assert(attr_float3.size() >= offset + size * 3); + for(size_t k = 0; k < size*3; k++) { attr_float3[offset+k] = (&tfm->x)[k]; } - attr_float3_offset += size * 4; + attr_float3_offset += size * 3; } else { float4 *data = mattr->data_float4(); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 41c9730e6fb..f117962a2ea 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -117,8 +117,7 @@ Transform TextureMapping::compute_transform() case NORMAL: /* no translation for normals, and inverse transpose */ mat = rmat*smat; - mat = transform_inverse(mat); - mat = transform_transpose(mat); + mat = transform_transposed_inverse(mat); break; } @@ -153,7 +152,6 @@ void TextureMapping::compile(SVMCompiler& compiler, int offset_in, int offset_ou compiler.add_node(tfm.x); compiler.add_node(tfm.y); compiler.add_node(tfm.z); - compiler.add_node(tfm.w); if(use_minmax) { compiler.add_node(NODE_MIN_MAX, offset_out, offset_out); @@ -193,9 +191,7 @@ void TextureMapping::compile_end(SVMCompiler& compiler, ShaderInput *vector_in, void TextureMapping::compile(OSLCompiler &compiler) { if(!skip()) { - Transform tfm = transform_transpose(compute_transform()); - - compiler.parameter("mapping", tfm); + compiler.parameter("mapping", compute_transform()); compiler.parameter("use_mapping", 1); } } @@ -1434,7 +1430,6 @@ void PointDensityTextureNode::compile(SVMCompiler& compiler) compiler.add_node(tfm.x); compiler.add_node(tfm.y); compiler.add_node(tfm.z); - compiler.add_node(tfm.w); } } else { @@ -1478,7 +1473,7 @@ void PointDensityTextureNode::compile(OSLCompiler& compiler) compiler.parameter("filename", string_printf("@%d", slot).c_str()); } if(space == NODE_TEX_VOXEL_SPACE_WORLD) { - compiler.parameter("mapping", transform_transpose(tfm)); + compiler.parameter("mapping", tfm); compiler.parameter("use_mapping", 1); } compiler.parameter(this, "interpolation"); @@ -1558,8 +1553,7 @@ void MappingNode::compile(SVMCompiler& compiler) void MappingNode::compile(OSLCompiler& compiler) { - Transform tfm = transform_transpose(tex_mapping.compute_transform()); - compiler.parameter("Matrix", tfm); + compiler.parameter("Matrix", tex_mapping.compute_transform()); compiler.parameter_point("mapping_min", tex_mapping.min); compiler.parameter_point("mapping_max", tex_mapping.max); compiler.parameter("use_minmax", tex_mapping.use_minmax); @@ -3220,7 +3214,6 @@ void TextureCoordinateNode::compile(SVMCompiler& compiler) compiler.add_node(ob_itfm.x); compiler.add_node(ob_itfm.y); compiler.add_node(ob_itfm.z); - compiler.add_node(ob_itfm.w); } } @@ -3259,7 +3252,7 @@ void TextureCoordinateNode::compile(OSLCompiler& compiler) if(compiler.output_type() == SHADER_TYPE_VOLUME) compiler.parameter("is_volume", true); compiler.parameter(this, "use_transform"); - Transform ob_itfm = transform_transpose(transform_inverse(ob_tfm)); + Transform ob_itfm = transform_transposed_inverse(ob_tfm); compiler.parameter("object_itfm", ob_itfm); compiler.parameter(this, "from_dupli"); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index f632f7c4a53..92c7e9cf1b8 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -132,7 +132,7 @@ void Object::apply_transform(bool apply_to_motion) /* store matrix to transform later. when accessing these as attributes we * do not want the transform to be applied for consistency between static * and dynamic BVH, so we do it on packing. */ - mesh->transform_normal = transform_transpose(transform_inverse(tfm)); + mesh->transform_normal = transform_transposed_inverse(tfm); /* apply to mesh vertices */ for(size_t i = 0; i < mesh->verts.size(); i++) @@ -290,7 +290,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s int object_index) { KernelObject& kobject = state->objects[object_index]; - float4 *objects_vector = state->objects_vector; + Transform *objects_vector = state->objects_vector; Mesh *mesh = ob->mesh; uint flag = 0; @@ -357,11 +357,8 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s } } - /* OBJECT_TRANSFORM */ - memcpy(&kobject.tfm.pre, &tfm, sizeof(float4)*3); - /* OBJECT_INVERSE_TRANSFORM */ - memcpy(&kobject.tfm.mid, &itfm, sizeof(float4)*3); - /* OBJECT_PROPERTIES */ + memcpy(&kobject.tfm.pre, &tfm, sizeof(tfm)); + memcpy(&kobject.tfm.mid, &itfm, sizeof(itfm)); kobject.surface_area = surface_area; kobject.pass_id = pass_id; kobject.random_number = random_number; @@ -395,8 +392,8 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s mtfm.post = mtfm.post * itfm; } - memcpy(&objects_vector[object_index*OBJECT_VECTOR_SIZE+0], &mtfm.pre, sizeof(float4)*3); - memcpy(&objects_vector[object_index*OBJECT_VECTOR_SIZE+3], &mtfm.post, sizeof(float4)*3); + objects_vector[object_index*OBJECT_VECTOR_SIZE+0] = mtfm.pre; + objects_vector[object_index*OBJECT_VECTOR_SIZE+1] = mtfm.post; } else if(state->need_motion == Scene::MOTION_BLUR) { if(ob->use_motion) { diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 54046d7069f..59c1d916595 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -135,7 +135,7 @@ protected: /* Packed object arrays. Those will be filled in. */ uint *object_flag; KernelObject *objects; - float4 *objects_vector; + Transform *objects_vector; /* Flags which will be synchronized to Integrator. */ bool have_motion; diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 9e931280691..f1a22350060 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -34,6 +34,7 @@ #include "util/util_md5.h" #include "util/util_path.h" #include "util/util_progress.h" +#include "util/util_projection.h" #endif @@ -832,7 +833,9 @@ void OSLCompiler::parameter(ShaderNode* node, const char *name) case SocketType::TRANSFORM: { Transform value = node->get_transform(socket); - ss->Parameter(uname, TypeDesc::TypeMatrix, &value); + ProjectionTransform projection(value); + projection = projection_transpose(projection); + ss->Parameter(uname, TypeDesc::TypeMatrix, &projection); break; } case SocketType::BOOLEAN_ARRAY: @@ -900,7 +903,11 @@ void OSLCompiler::parameter(ShaderNode* node, const char *name) case SocketType::TRANSFORM_ARRAY: { const array& value = node->get_transform_array(socket); - ss->Parameter(uname, array_typedesc(TypeDesc::TypeMatrix, value.size()), value.data()); + array fvalue(value.size()); + for(size_t i = 0; i < value.size(); i++) { + fvalue[i] = projection_transpose(ProjectionTransform(value[i])); + } + ss->Parameter(uname, array_typedesc(TypeDesc::TypeMatrix, fvalue.size()), fvalue.data()); break; } case SocketType::CLOSURE: @@ -967,7 +974,9 @@ void OSLCompiler::parameter(const char *name, ustring s) void OSLCompiler::parameter(const char *name, const Transform& tfm) { OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys; - ss->Parameter(name, TypeDesc::TypeMatrix, (float*)&tfm); + ProjectionTransform projection(tfm); + projection = projection_transpose(projection); + ss->Parameter(name, TypeDesc::TypeMatrix, (float*)&projection); } void OSLCompiler::parameter_array(const char *name, const float f[], int arraylen) diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 316ffeb1092..cd5c9c1dadc 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -87,7 +87,7 @@ public: /* objects */ device_vector objects; - device_vector objects_vector; + device_vector objects_vector; /* attributes */ device_vector attributes_map; diff --git a/intern/cycles/util/util_projection.h b/intern/cycles/util/util_projection.h index 8073568046d..dbcb9877a48 100644 --- a/intern/cycles/util/util_projection.h +++ b/intern/cycles/util/util_projection.h @@ -35,7 +35,7 @@ typedef struct ProjectionTransform { : x(tfm.x), y(tfm.y), z(tfm.z), - w(tfm.w) + w(make_float4(0.0f, 0.0f, 0.0f, 1.0f)) { } #endif @@ -69,6 +69,12 @@ ccl_device_inline float3 transform_perspective_direction(const ProjectionTransfo #ifndef __KERNEL_GPU__ +ccl_device_inline Transform projection_to_transform(const ProjectionTransform& a) +{ + Transform tfm = {a.x, a.y, a.z}; + return tfm; +} + ccl_device_inline ProjectionTransform projection_transpose(const ProjectionTransform& a) { ProjectionTransform t; @@ -81,12 +87,7 @@ ccl_device_inline ProjectionTransform projection_transpose(const ProjectionTrans return t; } -ccl_device_inline ProjectionTransform projection_inverse(const ProjectionTransform& a) -{ - Transform t = {a.x, a.y, a.z, a.w}; - t = transform_inverse(t); - return ProjectionTransform(t); -} +ProjectionTransform projection_inverse(const ProjectionTransform& a); ccl_device_inline ProjectionTransform make_projection( float a, float b, float c, float d, diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/util_transform.cpp index edb53cc06b7..e4e0d627497 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/util_transform.cpp @@ -46,6 +46,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "util/util_projection.h" #include "util/util_transform.h" #include "util/util_boundbox.h" @@ -129,9 +130,9 @@ static bool transform_matrix4_gj_inverse(float R[][4], float M[][4]) return true; } -Transform transform_inverse(const Transform& tfm) +ProjectionTransform projection_inverse(const ProjectionTransform& tfm) { - Transform tfmR = transform_identity(); + ProjectionTransform tfmR = projection_identity(); float M[4][4], R[4][4]; memcpy(R, &tfmR, sizeof(R)); @@ -145,7 +146,7 @@ Transform transform_inverse(const Transform& tfm) M[2][2] += 1e-8f; if(UNLIKELY(!transform_matrix4_gj_inverse(R, M))) { - return transform_identity(); + return projection_identity(); } } @@ -154,6 +155,19 @@ Transform transform_inverse(const Transform& tfm) return tfmR; } +Transform transform_inverse(const Transform& tfm) +{ + ProjectionTransform projection(tfm); + return projection_to_transform(projection_inverse(projection)); +} + +Transform transform_transposed_inverse(const Transform& tfm) +{ + ProjectionTransform projection(tfm); + ProjectionTransform iprojection = projection_inverse(projection); + return projection_to_transform(projection_transpose(iprojection)); +} + /* Motion Transform */ float4 transform_to_quat(const Transform& tfm) @@ -209,7 +223,7 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf /* extract rotation */ Transform M = *tfm; - M.x.w = 0.0f; M.y.w = 0.0f; M.z.w = 0.0f; M.w.w = 1.0f; + M.x.w = 0.0f; M.y.w = 0.0f; M.z.w = 0.0f; Transform R = M; float norm; @@ -217,9 +231,9 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf do { Transform Rnext; - Transform Rit = transform_inverse(transform_transpose(R)); + Transform Rit = transform_transposed_inverse(R); - for(int i = 0; i < 4; i++) + for(int i = 0; i < 3; i++) for(int j = 0; j < 4; j++) Rnext[i][j] = 0.5f * (R[i][j] + Rit[i][j]); diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index 65166e325e8..12f8405f233 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -26,10 +26,10 @@ CCL_NAMESPACE_BEGIN -/* Data Types */ +/* Affine transformation, stored as 4x3 matrix. */ typedef struct Transform { - float4 x, y, z, w; /* rows */ + float4 x, y, z; #ifndef __KERNEL_GPU__ float4 operator[](int i) const { return *(&x + i); } @@ -69,7 +69,7 @@ ccl_device_inline float3 transform_point(const Transform *t, const float3 a) x = _mm_loadu_ps(&t->x.x); y = _mm_loadu_ps(&t->y.x); z = _mm_loadu_ps(&t->z.x); - w = _mm_loadu_ps(&t->w.x); + w = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f); _MM_TRANSPOSE4_PS(x, y, z, w); @@ -125,29 +125,15 @@ ccl_device_inline float3 transform_direction_transposed(const Transform *t, cons return make_float3(dot(x, a), dot(y, a), dot(z, a)); } -ccl_device_inline Transform transform_transpose(const Transform a) -{ - Transform t; - - t.x.x = a.x.x; t.x.y = a.y.x; t.x.z = a.z.x; t.x.w = a.w.x; - t.y.x = a.x.y; t.y.y = a.y.y; t.y.z = a.z.y; t.y.w = a.w.y; - t.z.x = a.x.z; t.z.y = a.y.z; t.z.z = a.z.z; t.z.w = a.w.z; - t.w.x = a.x.w; t.w.y = a.y.w; t.w.z = a.z.w; t.w.w = a.w.w; - - return t; -} - ccl_device_inline Transform make_transform(float a, float b, float c, float d, float e, float f, float g, float h, - float i, float j, float k, float l, - float m, float n, float o, float p) + float i, float j, float k, float l) { Transform t; t.x.x = a; t.x.y = b; t.x.z = c; t.x.w = d; t.y.x = e; t.y.y = f; t.y.z = g; t.y.w = h; t.z.x = i; t.z.y = j; t.z.z = k; t.z.w = l; - t.w.x = m; t.w.y = n; t.w.z = o; t.w.w = p; return t; } @@ -161,21 +147,22 @@ ccl_device_inline Transform make_transform_frame(float3 N) const float3 dy = normalize(cross(N, dx)); return make_transform(dx.x, dx.y, dx.z, 0.0f, dy.x, dy.y, dy.z, 0.0f, - N.x , N.y, N.z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); + N.x , N.y, N.z, 0.0f); } #ifndef __KERNEL_GPU__ ccl_device_inline Transform operator*(const Transform a, const Transform b) { - Transform c = transform_transpose(b); - Transform t; + float4 c_x = make_float4(b.x.x, b.y.x, b.z.x, 0.0f); + float4 c_y = make_float4(b.x.y, b.y.y, b.z.y, 0.0f); + float4 c_z = make_float4(b.x.z, b.y.z, b.z.z, 0.0f); + float4 c_w = make_float4(b.x.w, b.y.w, b.z.w, 1.0f); - t.x = make_float4(dot(a.x, c.x), dot(a.x, c.y), dot(a.x, c.z), dot(a.x, c.w)); - t.y = make_float4(dot(a.y, c.x), dot(a.y, c.y), dot(a.y, c.z), dot(a.y, c.w)); - t.z = make_float4(dot(a.z, c.x), dot(a.z, c.y), dot(a.z, c.z), dot(a.z, c.w)); - t.w = make_float4(dot(a.w, c.x), dot(a.w, c.y), dot(a.w, c.z), dot(a.w, c.w)); + Transform t; + t.x = make_float4(dot(a.x, c_x), dot(a.x, c_y), dot(a.x, c_z), dot(a.x, c_w)); + t.y = make_float4(dot(a.y, c_x), dot(a.y, c_y), dot(a.y, c_z), dot(a.y, c_w)); + t.z = make_float4(dot(a.z, c_x), dot(a.z, c_y), dot(a.z, c_z), dot(a.z, c_w)); return t; } @@ -185,7 +172,6 @@ ccl_device_inline void print_transform(const char *label, const Transform& t) print_float4(label, t.x); print_float4(label, t.y); print_float4(label, t.z); - print_float4(label, t.w); printf("\n"); } @@ -194,8 +180,7 @@ ccl_device_inline Transform transform_translate(float3 t) return make_transform( 1, 0, 0, t.x, 0, 1, 0, t.y, - 0, 0, 1, t.z, - 0, 0, 0, 1); + 0, 0, 1, t.z); } ccl_device_inline Transform transform_translate(float x, float y, float z) @@ -208,8 +193,7 @@ ccl_device_inline Transform transform_scale(float3 s) return make_transform( s.x, 0, 0, 0, 0, s.y, 0, 0, - 0, 0, s.z, 0, - 0, 0, 0, 1); + 0, 0, s.z, 0); } ccl_device_inline Transform transform_scale(float x, float y, float z) @@ -239,9 +223,7 @@ ccl_device_inline Transform transform_rotate(float angle, float3 axis) axis.z*axis.x*t - s*axis.y, axis.z*axis.y*t + s*axis.x, axis.z*axis.z*t + c, - 0.0f, - - 0.0f, 0.0f, 0.0f, 1.0f); + 0.0f); } /* Euler is assumed to be in XYZ order. */ @@ -281,20 +263,20 @@ ccl_device_inline void transform_set_column(Transform *t, int column, float3 val } Transform transform_inverse(const Transform& a); +Transform transform_transposed_inverse(const Transform& a); ccl_device_inline bool transform_uniform_scale(const Transform& tfm, float& scale) { /* the epsilon here is quite arbitrary, but this function is only used for - * surface area and bump, where we except it to not be so sensitive */ - Transform ttfm = transform_transpose(tfm); + * surface area and bump, where we expect it to not be so sensitive */ float eps = 1e-6f; float sx = len_squared(float4_to_float3(tfm.x)); float sy = len_squared(float4_to_float3(tfm.y)); float sz = len_squared(float4_to_float3(tfm.z)); - float stx = len_squared(float4_to_float3(ttfm.x)); - float sty = len_squared(float4_to_float3(ttfm.y)); - float stz = len_squared(float4_to_float3(ttfm.z)); + float stx = len_squared(transform_get_column(&tfm, 0)); + float sty = len_squared(transform_get_column(&tfm, 1)); + float stz = len_squared(transform_get_column(&tfm, 2)); if(fabsf(sx - sy) < eps && fabsf(sx - sz) < eps && fabsf(sx - stx) < eps && fabsf(sx - sty) < eps && @@ -330,7 +312,6 @@ ccl_device_inline Transform transform_clear_scale(const Transform& tfm) ccl_device_inline Transform transform_empty() { return make_transform( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); @@ -389,7 +370,6 @@ ccl_device_inline Transform transform_quick_inverse(Transform M) R.x = make_float4(Rx.x, Rx.y, Rx.z, dot(Rx, T)); R.y = make_float4(Ry.x, Ry.y, Ry.z, dot(Ry, T)); R.z = make_float4(Rz.x, Rz.y, Rz.z, dot(Rz, T)); - R.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); return R; } @@ -427,7 +407,6 @@ ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransfo tfm->x = make_float4(dot(rotation_x, scale_x), dot(rotation_x, scale_y), dot(rotation_x, scale_z), decomp->y.x); tfm->y = make_float4(dot(rotation_y, scale_x), dot(rotation_y, scale_y), dot(rotation_y, scale_z), decomp->y.y); tfm->z = make_float4(dot(rotation_z, scale_x), dot(rotation_z, scale_y), dot(rotation_z, scale_z), decomp->y.z); - tfm->w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); } ccl_device void transform_motion_interpolate(Transform *tfm, const ccl_global DecomposedMotionTransform *motion, float t) From 6c4ddfc5d9833b6c4f0ae3da93645556fee97caf Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 01:15:02 +0100 Subject: [PATCH 12/21] Code refactor: don't expose UpdateObjectTransformState in header. --- intern/cycles/render/object.cpp | 66 ++++++++++++++++++++++++++------- intern/cycles/render/object.h | 44 +--------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 92c7e9cf1b8..b9702cfe531 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -33,6 +33,48 @@ CCL_NAMESPACE_BEGIN +/* Global state of object transform update. */ + +struct UpdateObjectTransformState { + /* Global state used by device_update_object_transform(). + * Common for both threaded and non-threaded update. + */ + + /* Type of the motion required by the scene settings. */ + Scene::MotionType need_motion; + + /* Mapping from particle system to a index in packed particle array. + * Only used for read. + */ + map particle_offset; + + /* Mesh area. + * Used to avoid calculation of mesh area multiple times. Used for both + * read and write. Acquire surface_area_lock to keep it all thread safe. + */ + map surface_area_map; + + /* Packed object arrays. Those will be filled in. */ + uint *object_flag; + KernelObject *objects; + Transform *objects_vector; + + /* Flags which will be synchronized to Integrator. */ + bool have_motion; + bool have_curves; + + /* ** Scheduling queue. ** */ + + Scene *scene; + + /* Some locks to keep everything thread-safe. */ + thread_spin_lock queue_lock; + thread_spin_lock surface_area_lock; + + /* First unused object index in the queue. */ + int queue_start_object; +}; + /* Object */ NODE_DEFINE(Object) @@ -475,7 +517,6 @@ void ObjectManager::device_update_object_transform_task( void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene, - uint *object_flag, Progress& progress) { UpdateObjectTransformState state; @@ -485,14 +526,13 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, state.scene = scene; state.queue_start_object = 0; - state.object_flag = object_flag; state.objects = dscene->objects.alloc(scene->objects.size()); + state.object_flag = dscene->object_flag.alloc(scene->objects.size()); + state.objects_vector = NULL; + if(state.need_motion == Scene::MOTION_PASS) { state.objects_vector = dscene->objects_vector.alloc(OBJECT_VECTOR_SIZE*scene->objects.size()); } - else { - state.objects_vector = NULL; - } /* Particle system device offsets * 0 is dummy particle, index starts at 1. @@ -554,12 +594,9 @@ void ObjectManager::device_update(Device *device, DeviceScene *dscene, Scene *sc if(scene->objects.size() == 0) return; - /* object info flag */ - uint *object_flag = dscene->object_flag.alloc(scene->objects.size()); - /* set object transform matrices, before applying static transforms */ progress.set_status("Updating Objects", "Copying Transformations to device"); - device_update_transforms(dscene, scene, object_flag, progress); + device_update_transforms(dscene, scene, progress); if(progress.get_cancel()) return; @@ -567,7 +604,7 @@ void ObjectManager::device_update(Device *device, DeviceScene *dscene, Scene *sc /* todo: do before to support getting object level coords? */ if(scene->params.bvh_type == SceneParams::BVH_STATIC) { progress.set_status("Updating Objects", "Applying Static Transformations"); - apply_static_transforms(dscene, scene, object_flag, progress); + apply_static_transforms(dscene, scene, progress); } } @@ -586,9 +623,10 @@ void ObjectManager::device_update_flags(Device *, if(scene->objects.size() == 0) return; - /* object info flag */ + /* Object info flag. */ uint *object_flag = dscene->object_flag.data(); + /* Object volume intersection. */ vector volume_objects; bool has_volume_objects = false; foreach(Object *object, scene->objects) { @@ -642,7 +680,7 @@ void ObjectManager::device_update_flags(Device *, ++object_index; } - /* allocate object flag */ + /* Copy object flag. */ dscene->object_flag.copy_to_device(); } @@ -690,7 +728,7 @@ void ObjectManager::device_free(Device *, DeviceScene *dscene) dscene->object_flag.free(); } -void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, uint *object_flag, Progress& progress) +void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress& progress) { /* todo: normals and displacement should be done before applying transform! */ /* todo: create objects/meshes in right order! */ @@ -714,6 +752,8 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, u if(progress.get_cancel()) return; + uint *object_flag = dscene->object_flag.data(); + /* apply transforms for objects with single user meshes */ foreach(Object *object, scene->objects) { /* Annoying feedback loop here: we can't use is_instanced() because diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 59c1d916595..3f6d120b372 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -35,6 +35,7 @@ class ParticleSystem; class Progress; class Scene; struct Transform; +struct UpdateObjectTransformState; /* Object */ @@ -95,7 +96,6 @@ public: void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress); void device_update_transforms(DeviceScene *dscene, Scene *scene, - uint *object_flag, Progress& progress); void device_update_flags(Device *device, @@ -109,49 +109,9 @@ public: void tag_update(Scene *scene); - void apply_static_transforms(DeviceScene *dscene, Scene *scene, uint *object_flag, Progress& progress); + void apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress& progress); protected: - /* Global state of object transform update. */ - struct UpdateObjectTransformState { - /* Global state used by device_update_object_transform(). - * Common for both threaded and non-threaded update. - */ - - /* Type of the motion required by the scene settings. */ - Scene::MotionType need_motion; - - /* Mapping from particle system to a index in packed particle array. - * Only used for read. - */ - map particle_offset; - - /* Mesh area. - * Used to avoid calculation of mesh area multiple times. Used for both - * read and write. Acquire surface_area_lock to keep it all thread safe. - */ - map surface_area_map; - - /* Packed object arrays. Those will be filled in. */ - uint *object_flag; - KernelObject *objects; - Transform *objects_vector; - - /* Flags which will be synchronized to Integrator. */ - bool have_motion; - bool have_curves; - - /* ** Scheduling queue. ** */ - - Scene *scene; - - /* Some locks to keep everything thread-safe. */ - thread_spin_lock queue_lock; - thread_spin_lock surface_area_lock; - - /* First unused object index in the queue. */ - int queue_start_object; - }; void device_update_object_transform(UpdateObjectTransformState *state, Object *ob, const int object_index); From 267d8923265a284c5d9a462e1d86305d613fcad8 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 00:37:07 +0100 Subject: [PATCH 13/21] Code refactor: motion blur cleanups, preparing for next commit. --- intern/cycles/blender/blender_curves.cpp | 18 ++++---- intern/cycles/blender/blender_mesh.cpp | 52 ++++++---------------- intern/cycles/blender/blender_object.cpp | 12 +++-- intern/cycles/blender/blender_sync.h | 2 +- intern/cycles/kernel/geom/geom_object.h | 10 ++--- intern/cycles/kernel/geom/geom_primitive.h | 12 ++--- intern/cycles/kernel/kernel_camera.h | 4 +- intern/cycles/kernel/kernel_textures.h | 4 +- intern/cycles/kernel/kernel_types.h | 14 +++--- intern/cycles/render/camera.cpp | 52 +++++++++++----------- intern/cycles/render/camera.h | 1 - intern/cycles/render/mesh.cpp | 26 +++++++++++ intern/cycles/render/mesh.h | 5 +++ intern/cycles/render/object.cpp | 39 +++++----------- intern/cycles/render/object.h | 4 +- intern/cycles/render/scene.cpp | 4 +- intern/cycles/render/scene.h | 4 +- 17 files changed, 125 insertions(+), 138 deletions(-) diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index f7cb0b66d29..daccb89f5a2 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -633,10 +633,10 @@ static void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CDa } } -static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int time_index) +static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int motion_step) { VLOG(1) << "Exporting curve motion segments for mesh " << mesh->name - << ", time index " << time_index; + << ", motion step " << motion_step; /* find attribute */ Attribute *attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -651,7 +651,7 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int /* export motion vectors for curve keys */ size_t numkeys = mesh->curve_keys.size(); - float4 *mP = attr_mP->data_float4() + time_index*numkeys; + float4 *mP = attr_mP->data_float4() + motion_step*numkeys; bool have_motion = false; int i = 0; @@ -702,12 +702,12 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int } mesh->curve_attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); } - else if(time_index > 0) { - VLOG(1) << "Filling in new motion vertex position for time_index " - << time_index; + else if(motion_step > 0) { + VLOG(1) << "Filling in new motion vertex position for motion_step " + << motion_step; /* motion, fill up previous steps that we might have skipped because * they had no motion, but we need them anyway now */ - for(int step = 0; step < time_index; step++) { + for(int step = 0; step < motion_step; step++) { float4 *mP = attr_mP->data_float4() + step*numkeys; for(int key = 0; key < numkeys; key++) { @@ -888,7 +888,7 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh& b_mesh, BL::Object& b_ob, bool motion, - int time_index) + int motion_step) { if(!motion) { /* Clear stored curve data */ @@ -951,7 +951,7 @@ void BlenderSync::sync_curves(Mesh *mesh, } else { if(motion) - ExportCurveSegmentsMotion(mesh, &CData, time_index); + ExportCurveSegmentsMotion(mesh, &CData, motion_step); else ExportCurveSegments(scene, mesh, &CData); } diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 998390e92a9..7d6ca18b074 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -1248,36 +1248,10 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, if(mesh_synced.find(mesh) == mesh_synced.end()) return; - /* for motion pass always compute, for motion blur it can be disabled */ - int time_index = 0; - - if(scene->need_motion() == Scene::MOTION_BLUR) { - if(!mesh->use_motion_blur) - return; - - /* see if this mesh needs motion data at this time */ - vector object_times = object->motion_times(); - bool found = false; - - foreach(float object_time, object_times) { - if(motion_time == object_time) { - found = true; - break; - } - else - time_index++; - } - - if(!found) - return; - } - else { - if(motion_time == -1.0f) - time_index = 0; - else if(motion_time == 1.0f) - time_index = 1; - else - return; + /* Find time matching motion step required by mesh. */ + int motion_step = mesh->motion_step(motion_time); + if(motion_step < 0) { + return; } /* skip empty meshes */ @@ -1319,9 +1293,9 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, float3 *P = &mesh->verts[0]; float3 *N = (attr_N)? attr_N->data_float3(): NULL; - memcpy(attr_mP->data_float3() + time_index*numverts, P, sizeof(float3)*numverts); + memcpy(attr_mP->data_float3() + motion_step*numverts, P, sizeof(float3)*numverts); if(attr_mN) - memcpy(attr_mN->data_float3() + time_index*numverts, N, sizeof(float3)*numverts); + memcpy(attr_mN->data_float3() + motion_step*numverts, N, sizeof(float3)*numverts); } } @@ -1331,7 +1305,7 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, if(attr_mP) { float3 *keys = &mesh->curve_keys[0]; - memcpy(attr_mP->data_float3() + time_index*numkeys, keys, sizeof(float3)*numkeys); + memcpy(attr_mP->data_float3() + motion_step*numkeys, keys, sizeof(float3)*numkeys); } } @@ -1354,8 +1328,8 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, new_attribute = true; } /* Load vertex data from mesh. */ - float3 *mP = attr_mP->data_float3() + time_index*numverts; - float3 *mN = (attr_mN)? attr_mN->data_float3() + time_index*numverts: NULL; + float3 *mP = attr_mP->data_float3() + motion_step*numverts; + float3 *mN = (attr_mN)? attr_mN->data_float3() + motion_step*numverts: NULL; /* NOTE: We don't copy more that existing amount of vertices to prevent * possible memory corruption. */ @@ -1384,13 +1358,13 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, if(attr_mN) mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_NORMAL); } - else if(time_index > 0) { + else if(motion_step > 0) { VLOG(1) << "Filling deformation motion for object " << b_ob.name(); /* motion, fill up previous steps that we might have skipped because * they had no motion, but we need them anyway now */ float3 *P = &mesh->verts[0]; float3 *N = (attr_N)? attr_N->data_float3(): NULL; - for(int step = 0; step < time_index; step++) { + for(int step = 0; step < motion_step; step++) { memcpy(attr_mP->data_float3() + step*numverts, P, sizeof(float3)*numverts); if(attr_mN) memcpy(attr_mN->data_float3() + step*numverts, N, sizeof(float3)*numverts); @@ -1400,7 +1374,7 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, else { if(b_mesh.vertices.length() != numverts) { VLOG(1) << "Topology differs, discarding motion blur for object " - << b_ob.name() << " at time " << time_index; + << b_ob.name() << " at time " << motion_step; memcpy(mP, &mesh->verts[0], sizeof(float3)*numverts); if(mN != NULL) { memcpy(mN, attr_N->data_float3(), sizeof(float3)*numverts); @@ -1411,7 +1385,7 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, /* hair motion */ if(numkeys) - sync_curves(mesh, b_mesh, b_ob, true, time_index); + sync_curves(mesh, b_mesh, b_ob, true, motion_step); /* free derived mesh */ b_data.meshes.remove(b_mesh, false, true, false); diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 38ef1bc5249..45309292f0b 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -396,7 +396,6 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, /* motion blur */ if(scene->need_motion() == Scene::MOTION_BLUR && object->mesh) { Mesh *mesh = object->mesh; - mesh->use_motion_blur = false; if(object_use_motion(b_parent, b_ob)) { @@ -405,9 +404,9 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, mesh->use_motion_blur = true; } - vector times = object->motion_times(); - foreach(float time, times) - motion_times.insert(time); + for(size_t step = 0; step < mesh->motion_steps - 1; step++) { + motion_times.insert(mesh->motion_time(step)); + } } } @@ -694,6 +693,11 @@ void BlenderSync::sync_motion(BL::RenderSettings& b_render, /* note iteration over motion_times set happens in sorted order */ foreach(float relative_time, motion_times) { + /* center time is already handled. */ + if(relative_time == 0.0f) { + continue; + } + VLOG(1) << "Synchronizing motion for the relative time " << relative_time << "."; diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index e7b71ae9310..1e7b0b32518 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -118,7 +118,7 @@ private: BL::Mesh& b_mesh, BL::Object& b_ob, bool motion, - int time_index = 0); + int motion_step = 0); Object *sync_object(BL::Object& b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject& b_dupli_ob, diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 62bc46dc21b..a7191c50e97 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -32,8 +32,8 @@ enum ObjectTransform { }; enum ObjectVectorTransform { - OBJECT_VECTOR_MOTION_PRE = 0, - OBJECT_VECTOR_MOTION_POST = 1 + OBJECT_PASS_MOTION_PRE = 0, + OBJECT_PASS_MOTION_POST = 1 }; /* Object to world space transformation */ @@ -68,10 +68,10 @@ ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bo /* Object to world space transformation for motion vectors */ -ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int object, enum ObjectVectorTransform type) +ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals *kg, int object, enum ObjectVectorTransform type) { - int offset = object*OBJECT_VECTOR_SIZE + (int)type; - return kernel_tex_fetch(__objects_vector, offset); + int offset = object*OBJECT_MOTION_PASS_SIZE + (int)type; + return kernel_tex_fetch(__object_motion_pass, offset); } /* Motion blurred object transformations */ diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index 4e84446bae0..c159be92885 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -193,10 +193,10 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * * transformation was set match the world/object space of motion_pre/post */ Transform tfm; - tfm = object_fetch_vector_transform(kg, sd->object, OBJECT_VECTOR_MOTION_PRE); + tfm = object_fetch_motion_pass_transform(kg, sd->object, OBJECT_PASS_MOTION_PRE); motion_pre = transform_point(&tfm, motion_pre); - tfm = object_fetch_vector_transform(kg, sd->object, OBJECT_VECTOR_MOTION_POST); + tfm = object_fetch_motion_pass_transform(kg, sd->object, OBJECT_PASS_MOTION_POST); motion_post = transform_point(&tfm, motion_post); float3 motion_center; @@ -207,10 +207,10 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * ProjectionTransform projection = kernel_data.cam.worldtoraster; motion_center = transform_perspective(&projection, center); - projection = kernel_data.cam.perspective_motion.pre; + projection = kernel_data.cam.perspective_pre; motion_pre = transform_perspective(&projection, motion_pre); - projection = kernel_data.cam.perspective_motion.post; + projection = kernel_data.cam.perspective_post; motion_post = transform_perspective(&projection, motion_post); } else { @@ -220,13 +220,13 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * motion_center.x *= kernel_data.cam.width; motion_center.y *= kernel_data.cam.height; - tfm = kernel_data.cam.pass_motion.pre; + tfm = kernel_data.cam.motion_pass_pre; motion_pre = normalize(transform_point(&tfm, motion_pre)); motion_pre = float2_to_float3(direction_to_panorama(&kernel_data.cam, motion_pre)); motion_pre.x *= kernel_data.cam.width; motion_pre.y *= kernel_data.cam.height; - tfm = kernel_data.cam.pass_motion.post; + tfm = kernel_data.cam.motion_pass_post; motion_post = normalize(transform_point(&tfm, motion_post)); motion_post = float2_to_float3(direction_to_panorama(&kernel_data.cam, motion_post)); motion_post.x *= kernel_data.cam.width; diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 755c3472072..a0be0ba6454 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -54,13 +54,13 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo * interpolated field of view. */ if(ray->time < 0.5f) { - ProjectionTransform rastertocamera_pre = kernel_data.cam.perspective_motion.pre; + ProjectionTransform rastertocamera_pre = kernel_data.cam.perspective_pre; float3 Pcamera_pre = transform_perspective(&rastertocamera_pre, raster); Pcamera = interp(Pcamera_pre, Pcamera, ray->time * 2.0f); } else { - ProjectionTransform rastertocamera_post = kernel_data.cam.perspective_motion.post; + ProjectionTransform rastertocamera_post = kernel_data.cam.perspective_post; float3 Pcamera_post = transform_perspective(&rastertocamera_post, raster); Pcamera = interp(Pcamera, Pcamera_post, (ray->time - 0.5f) * 2.0f); diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 6e07d88008e..564b3f0f79c 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -32,7 +32,8 @@ KERNEL_TEX(float2, __prim_time) /* objects */ KERNEL_TEX(KernelObject, __objects) -KERNEL_TEX(Transform, __objects_vector) +KERNEL_TEX(Transform, __object_motion_pass) +KERNEL_TEX(uint, __object_flag) /* triangles */ KERNEL_TEX(uint, __tri_shader) @@ -66,7 +67,6 @@ KERNEL_TEX(KernelParticle, __particles) /* shaders */ KERNEL_TEX(uint4, __svm_nodes) KERNEL_TEX(KernelShader, __shaders) -KERNEL_TEX(uint, __object_flag) /* lookup tables */ KERNEL_TEX(float, __lookup_table) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 567f2a04028..6743cc62894 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -35,10 +35,10 @@ CCL_NAMESPACE_BEGIN /* Constants */ -#define OBJECT_VECTOR_SIZE 2 -#define FILTER_TABLE_SIZE 1024 -#define RAMP_TABLE_SIZE 256 -#define SHUTTER_TABLE_SIZE 256 +#define OBJECT_MOTION_PASS_SIZE 2 +#define FILTER_TABLE_SIZE 1024 +#define RAMP_TABLE_SIZE 256 +#define SHUTTER_TABLE_SIZE 256 #define BSSRDF_MIN_RADIUS 1e-8f #define BSSRDF_MAX_HITS 4 @@ -1205,10 +1205,12 @@ typedef struct KernelCamera { /* Stores changes in the projeciton matrix. Use for camera zoom motion * blur and motion pass output for perspective camera. */ - PerspectiveMotionTransform perspective_motion; + ProjectionTransform perspective_pre; + ProjectionTransform perspective_post; /* Transforms for motion pass. */ - MotionTransform pass_motion; + Transform motion_pass_pre; + Transform motion_pass_post; int shutter_table_offset; diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index 5e8c5db6c0e..a1c6a279956 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -302,23 +302,6 @@ void Camera::update(Scene *scene) frustum_top_normal = normalize(make_float3(0.0f, v.z, -v.y)); } - /* TODO(sergey): Support other types of camera. */ - if(type == CAMERA_PERSPECTIVE) { - /* TODO(sergey): Move to an utility function and de-duplicate with - * calculation above. - */ - ProjectionTransform screentocamera_pre = - projection_inverse(projection_perspective(fov_pre, - nearclip, - farclip)); - ProjectionTransform screentocamera_post = - projection_inverse(projection_perspective(fov_post, - nearclip, - farclip)); - perspective_motion.pre = screentocamera_pre * rastertoscreen; - perspective_motion.post = screentocamera_post * rastertoscreen; - } - /* Compute kernel camera data. */ KernelCamera *kcam = &kernel_camera; @@ -341,22 +324,22 @@ void Camera::update(Scene *scene) /* TODO(sergey): Support perspective (zoom, fov) motion. */ if(type == CAMERA_PANORAMA) { if(use_motion) { - kcam->pass_motion.pre = transform_inverse(motion.pre); - kcam->pass_motion.post = transform_inverse(motion.post); + kcam->motion_pass_pre = transform_inverse(motion.pre); + kcam->motion_pass_post = transform_inverse(motion.post); } else { - kcam->pass_motion.pre = kcam->worldtocamera; - kcam->pass_motion.post = kcam->worldtocamera; + kcam->motion_pass_pre = kcam->worldtocamera; + kcam->motion_pass_post = kcam->worldtocamera; } } else { if(use_motion) { - kcam->perspective_motion.pre = cameratoraster * transform_inverse(motion.pre); - kcam->perspective_motion.post = cameratoraster * transform_inverse(motion.post); + kcam->perspective_pre = cameratoraster * transform_inverse(motion.pre); + kcam->perspective_post = cameratoraster * transform_inverse(motion.post); } else { - kcam->perspective_motion.pre = worldtoraster; - kcam->perspective_motion.post = worldtoraster; + kcam->perspective_pre = worldtoraster; + kcam->perspective_post = worldtoraster; } } } @@ -365,8 +348,23 @@ void Camera::update(Scene *scene) transform_motion_decompose(&kcam->motion, &motion, &matrix); kcam->have_motion = 1; } - if(use_perspective_motion) { - kcam->perspective_motion = perspective_motion; + + /* TODO(sergey): Support other types of camera. */ + if(use_perspective_motion && type == CAMERA_PERSPECTIVE) { + /* TODO(sergey): Move to an utility function and de-duplicate with + * calculation above. + */ + ProjectionTransform screentocamera_pre = + projection_inverse(projection_perspective(fov_pre, + nearclip, + farclip)); + ProjectionTransform screentocamera_post = + projection_inverse(projection_perspective(fov_post, + nearclip, + farclip)); + + kcam->perspective_pre = screentocamera_pre * rastertoscreen; + kcam->perspective_post = screentocamera_post * rastertoscreen; kcam->have_perspective_motion = 1; } } diff --git a/intern/cycles/render/camera.h b/intern/cycles/render/camera.h index aa4e53a347b..8f49a372f24 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/render/camera.h @@ -144,7 +144,6 @@ public: MotionTransform motion; bool use_motion, use_perspective_motion; float fov_pre, fov_post; - PerspectiveMotionTransform perspective_motion; /* computed camera parameters */ ProjectionTransform screentoworld; diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 71cbf5c24b6..9166d3bcd5e 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -1117,6 +1117,32 @@ bool Mesh::has_true_displacement() const return false; } +float Mesh::motion_time(int step) const +{ + return (motion_steps > 1) ? 2.0f * step / (motion_steps - 1) - 1.0f : 0.0f; +} + +int Mesh::motion_step(float time) const +{ + if(motion_steps > 1) { + int attr_step = 0; + + for(int step = 0; step < motion_steps; step++) { + float step_time = motion_time(step); + if(step_time == time) { + return attr_step; + } + + /* Center step is stored in a separate attribute. */ + if(step != motion_steps / 2) { + attr_step++; + } + } + } + + return -1; +} + bool Mesh::need_build_bvh() const { return !transform_applied || has_surface_bssrdf; diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index c0d1513bee0..6042faccad5 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -304,6 +304,11 @@ public: bool has_motion_blur() const; bool has_true_displacement() const; + /* Convert between normalized -1..1 motion time and index + * in the VERTEX_MOTION attribute. */ + float motion_time(int step) const; + int motion_step(float time) const; + /* Check whether the mesh should have own BVH built separately. Briefly, * own BVH is needed for mesh, if: * diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index b9702cfe531..6201775b992 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -57,7 +57,8 @@ struct UpdateObjectTransformState { /* Packed object arrays. Those will be filled in. */ uint *object_flag; KernelObject *objects; - Transform *objects_vector; + Transform *object_motion_pass; + DecomposedTransform *object_motion; /* Flags which will be synchronized to Integrator. */ bool have_motion; @@ -274,27 +275,7 @@ void Object::tag_update(Scene *scene) scene->object_manager->need_update = true; } -vector Object::motion_times() -{ - /* compute times at which we sample motion for this object */ - vector times; - - if(!mesh || mesh->motion_steps == 1) - return times; - - int motion_steps = mesh->motion_steps; - - for(int step = 0; step < motion_steps; step++) { - if(step != motion_steps / 2) { - float time = 2.0f * step / (motion_steps - 1) - 1.0f; - times.push_back(time); - } - } - - return times; -} - -bool Object::is_traceable() +bool Object::is_traceable() const { /* Mesh itself can be empty,can skip all such objects. */ if(!bounds.valid() || bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) { @@ -332,7 +313,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s int object_index) { KernelObject& kobject = state->objects[object_index]; - Transform *objects_vector = state->objects_vector; + Transform *object_motion_pass = state->object_motion_pass; Mesh *mesh = ob->mesh; uint flag = 0; @@ -434,8 +415,8 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s mtfm.post = mtfm.post * itfm; } - objects_vector[object_index*OBJECT_VECTOR_SIZE+0] = mtfm.pre; - objects_vector[object_index*OBJECT_VECTOR_SIZE+1] = mtfm.post; + object_motion_pass[object_index*OBJECT_MOTION_PASS_SIZE+0] = mtfm.pre; + object_motion_pass[object_index*OBJECT_MOTION_PASS_SIZE+1] = mtfm.post; } else if(state->need_motion == Scene::MOTION_BLUR) { if(ob->use_motion) { @@ -528,10 +509,10 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, state.objects = dscene->objects.alloc(scene->objects.size()); state.object_flag = dscene->object_flag.alloc(scene->objects.size()); - state.objects_vector = NULL; + state.object_motion_pass = NULL; if(state.need_motion == Scene::MOTION_PASS) { - state.objects_vector = dscene->objects_vector.alloc(OBJECT_VECTOR_SIZE*scene->objects.size()); + state.object_motion_pass = dscene->object_motion_pass.alloc(OBJECT_MOTION_PASS_SIZE*scene->objects.size()); } /* Particle system device offsets @@ -574,7 +555,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, dscene->objects.copy_to_device(); if(state.need_motion == Scene::MOTION_PASS) { - dscene->objects_vector.copy_to_device(); + dscene->object_motion_pass.copy_to_device(); } dscene->data.bvh.have_motion = state.have_motion; @@ -724,7 +705,7 @@ void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Sc void ObjectManager::device_free(Device *, DeviceScene *dscene) { dscene->objects.free(); - dscene->objects_vector.free(); + dscene->object_motion_pass.free(); dscene->object_flag.free(); } diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 3f6d120b372..7cf1528c4a7 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -70,12 +70,10 @@ public: void compute_bounds(bool motion_blur); void apply_transform(bool apply_to_motion); - vector motion_times(); - /* Check whether object is traceable and it worth adding it to * kernel scene. */ - bool is_traceable(); + bool is_traceable() const; /* Combine object's visibility with all possible internal run-time * determined flags which denotes trace-time visibility. diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index f5b8e2fd6a6..e9b9b417e30 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -60,7 +60,8 @@ DeviceScene::DeviceScene(Device *device) curve_keys(device, "__curve_keys", MEM_TEXTURE), patches(device, "__patches", MEM_TEXTURE), objects(device, "__objects", MEM_TEXTURE), - objects_vector(device, "__objects_vector", MEM_TEXTURE), + object_motion_pass(device, "__object_motion_pass", MEM_TEXTURE), + object_flag(device, "__object_flag", MEM_TEXTURE), attributes_map(device, "__attributes_map", MEM_TEXTURE), attributes_float(device, "__attributes_float", MEM_TEXTURE), attributes_float3(device, "__attributes_float3", MEM_TEXTURE), @@ -72,7 +73,6 @@ DeviceScene::DeviceScene(Device *device) particles(device, "__particles", MEM_TEXTURE), svm_nodes(device, "__svm_nodes", MEM_TEXTURE), shaders(device, "__shaders", MEM_TEXTURE), - object_flag(device, "__object_flag", MEM_TEXTURE), lookup_table(device, "__lookup_table", MEM_TEXTURE), sobol_directions(device, "__sobol_directions", MEM_TEXTURE) { diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index cd5c9c1dadc..aebd84f92d8 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -87,7 +87,8 @@ public: /* objects */ device_vector objects; - device_vector objects_vector; + device_vector object_motion_pass; + device_vector object_flag; /* attributes */ device_vector attributes_map; @@ -107,7 +108,6 @@ public: /* shaders */ device_vector svm_nodes; device_vector shaders; - device_vector object_flag; /* lookup tables */ device_vector lookup_table; From 78c2063685cb6e0d0bcb895cf4eb70686455d596 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 01:36:09 +0100 Subject: [PATCH 14/21] Cycles: support arbitrary number of motion blur steps for cameras. --- intern/cycles/blender/addon/ui.py | 10 ++- intern/cycles/blender/blender_camera.cpp | 33 +++++---- intern/cycles/kernel/geom/geom_object.h | 2 +- intern/cycles/kernel/kernel_camera.h | 46 +++++++----- intern/cycles/kernel/kernel_compat_cpu.h | 1 + intern/cycles/kernel/kernel_compat_cuda.h | 1 + intern/cycles/kernel/kernel_compat_opencl.h | 3 +- intern/cycles/kernel/kernel_textures.h | 3 + intern/cycles/kernel/kernel_types.h | 4 +- intern/cycles/render/camera.cpp | 77 ++++++++++++++++----- intern/cycles/render/camera.h | 10 ++- intern/cycles/render/object.cpp | 9 ++- intern/cycles/render/scene.cpp | 1 + intern/cycles/render/scene.h | 3 + intern/cycles/render/session.cpp | 2 +- intern/cycles/util/util_transform.cpp | 20 +++--- intern/cycles/util/util_transform.h | 70 ++++++------------- intern/cycles/util/util_vector.h | 12 ++++ 18 files changed, 175 insertions(+), 132 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index e3076a8ec2c..707f8756f6f 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -809,7 +809,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): def poll(cls, context): ob = context.object if CyclesButtonsPanel.poll(context) and ob: - if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META'}: + if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA'}: return True if ob.dupli_type == 'GROUP' and ob.dupli_group: return True @@ -841,11 +841,9 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): layout.active = (rd.use_motion_blur and cob.use_motion_blur) row = layout.row() - row.prop(cob, "use_deform_motion", text="Deformation") - - sub = row.row() - sub.active = cob.use_deform_motion - sub.prop(cob, "motion_steps", text="Steps") + if ob.type != 'CAMERA': + row.prop(cob, "use_deform_motion", text="Deformation") + row.prop(cob, "motion_steps", text="Steps") class CYCLES_OBJECT_PT_cycles_settings(CyclesButtonsPanel, Panel): diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index e5f885c0012..909db8d9449 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -83,6 +83,8 @@ struct BlenderCamera { Transform matrix; float offscreen_dicing_scale; + + int motion_steps; }; static void blender_camera_init(BlenderCamera *bcam, @@ -226,6 +228,10 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->sensor_fit = BlenderCamera::HORIZONTAL; else bcam->sensor_fit = BlenderCamera::VERTICAL; + + if(object_use_motion(b_ob, b_ob)) { + bcam->motion_steps = object_motion_steps(b_ob); + } } else { /* from lamp not implemented yet */ @@ -453,9 +459,7 @@ static void blender_camera_sync(Camera *cam, cam->matrix = blender_camera_matrix(bcam->matrix, bcam->type, bcam->panorama_type); - cam->motion.pre = cam->matrix; - cam->motion.post = cam->matrix; - cam->use_motion = false; + cam->motion.resize(bcam->motion_steps, cam->matrix); cam->use_perspective_motion = false; cam->shuttertime = bcam->shuttertime; cam->fov_pre = cam->fov; @@ -564,20 +568,15 @@ void BlenderSync::sync_camera_motion(BL::RenderSettings& b_render, Transform tfm = get_transform(b_ob_matrix); tfm = blender_camera_matrix(tfm, cam->type, cam->panorama_type); - if(tfm != cam->matrix) { - VLOG(1) << "Camera " << b_ob.name() << " motion detected."; - if(motion_time == 0.0f) { - /* When motion blur is not centered in frame, cam->matrix gets reset. */ - cam->matrix = tfm; - } - else if(motion_time == -1.0f) { - cam->motion.pre = tfm; - cam->use_motion = true; - } - else if(motion_time == 1.0f) { - cam->motion.post = tfm; - cam->use_motion = true; - } + if(motion_time == 0.0f) { + /* When motion blur is not centered in frame, cam->matrix gets reset. */ + cam->matrix = tfm; + } + + /* Set transform in motion array. */ + int motion_step = cam->motion_step(motion_time); + if(motion_step >= 0) { + cam->motion[motion_step] = tfm; } if(cam->type == CAMERA_PERSPECTIVE) { diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index a7191c50e97..67d039e1bc7 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -82,7 +82,7 @@ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int const ccl_global DecomposedMotionTransform *motion = &kernel_tex_fetch(__objects, object).tfm; Transform tfm; - transform_motion_interpolate(&tfm, motion, time); + transform_motion_array_interpolate(&tfm, &motion->pre, 3, time); return tfm; } diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index a0be0ba6454..b73ad47dad3 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -91,11 +91,12 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo Transform cameratoworld = kernel_data.cam.cameratoworld; #ifdef __CAMERA_MOTION__ - if(kernel_data.cam.have_motion) { - ccl_constant DecomposedMotionTransform *motion = &kernel_data.cam.motion; - transform_motion_interpolate_constant(&cameratoworld, - motion, - ray->time); + if(kernel_data.cam.num_motion_steps) { + transform_motion_array_interpolate( + &cameratoworld, + kernel_tex_array(__camera_motion), + kernel_data.cam.num_motion_steps, + ray->time); } #endif @@ -197,11 +198,12 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl Transform cameratoworld = kernel_data.cam.cameratoworld; #ifdef __CAMERA_MOTION__ - if(kernel_data.cam.have_motion) { - ccl_constant DecomposedMotionTransform *motion = &kernel_data.cam.motion; - transform_motion_interpolate_constant(&cameratoworld, - motion, - ray->time); + if(kernel_data.cam.num_motion_steps) { + transform_motion_array_interpolate( + &cameratoworld, + kernel_tex_array(__camera_motion), + kernel_data.cam.num_motion_steps, + ray->time); } #endif @@ -227,6 +229,7 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl /* Panorama Camera */ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, + const ccl_global DecomposedTransform *cam_motion, float raster_x, float raster_y, float lens_u, float lens_v, ccl_addr_space Ray *ray) @@ -269,11 +272,12 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, Transform cameratoworld = cam->cameratoworld; #ifdef __CAMERA_MOTION__ - if(cam->have_motion) { - ccl_constant DecomposedMotionTransform *motion = &cam->motion; - transform_motion_interpolate_constant(&cameratoworld, - motion, - ray->time); + if(cam->num_motion_steps) { + transform_motion_array_interpolate( + &cameratoworld, + cam_motion, + cam->num_motion_steps, + ray->time); } #endif @@ -392,12 +396,16 @@ ccl_device_inline void camera_sample(KernelGlobals *kg, #endif /* sample */ - if(kernel_data.cam.type == CAMERA_PERSPECTIVE) + if(kernel_data.cam.type == CAMERA_PERSPECTIVE) { camera_sample_perspective(kg, raster_x, raster_y, lens_u, lens_v, ray); - else if(kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) + } + else if(kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) { camera_sample_orthographic(kg, raster_x, raster_y, lens_u, lens_v, ray); - else - camera_sample_panorama(&kernel_data.cam, raster_x, raster_y, lens_u, lens_v, ray); + } + else { + const ccl_global DecomposedTransform *cam_motion = kernel_tex_array(__camera_motion); + camera_sample_panorama(&kernel_data.cam, cam_motion, raster_x, raster_y, lens_u, lens_v, ray); + } } /* Utilities */ diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h index 61cd90e9d2a..d26b668cb11 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/kernel_compat_cpu.h @@ -118,6 +118,7 @@ template struct texture { #define kernel_tex_fetch_ssef(tex, index) (kg->tex.fetch_ssef(index)) #define kernel_tex_fetch_ssei(tex, index) (kg->tex.fetch_ssei(index)) #define kernel_tex_lookup(tex, t, offset, size) (kg->tex.lookup(t, offset, size)) +#define kernel_tex_array(tex) (kg->tex.data) #define kernel_data (kg->__data) diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/kernel_compat_cuda.h index 9bd7a572f5f..ac63bcf7ac9 100644 --- a/intern/cycles/kernel/kernel_compat_cuda.h +++ b/intern/cycles/kernel/kernel_compat_cuda.h @@ -137,6 +137,7 @@ ccl_device_inline uint ccl_num_groups(uint d) /* Use arrays for regular data. */ #define kernel_tex_fetch(t, index) t[(index)] +#define kernel_tex_array(t) (t) #define kernel_data __data diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h index b02e3bc576d..671c47e2225 100644 --- a/intern/cycles/kernel/kernel_compat_opencl.h +++ b/intern/cycles/kernel/kernel_compat_opencl.h @@ -144,7 +144,8 @@ /* data lookup defines */ #define kernel_data (*kg->data) -#define kernel_tex_fetch(tex, index) ((const ccl_global tex##_t*)(kg->buffers[kg->tex.cl_buffer] + kg->tex.data))[(index)] +#define kernel_tex_array(tex) ((const ccl_global tex##_t*)(kg->buffers[kg->tex.cl_buffer] + kg->tex.data)) +#define kernel_tex_fetch(tex, index) kernel_tex_array(tex)[(index)] /* define NULL */ #define NULL 0 diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 564b3f0f79c..56de5b27605 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -35,6 +35,9 @@ KERNEL_TEX(KernelObject, __objects) KERNEL_TEX(Transform, __object_motion_pass) KERNEL_TEX(uint, __object_flag) +/* cameras */ +KERNEL_TEX(DecomposedTransform, __camera_motion) + /* triangles */ KERNEL_TEX(uint, __tri_shader) KERNEL_TEX(float4, __tri_vnormal) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 6743cc62894..2c3b7ba29a4 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1173,7 +1173,7 @@ typedef struct KernelCamera { /* motion blur */ float shuttertime; - int have_motion, have_perspective_motion; + int num_motion_steps, have_perspective_motion; /* clipping */ float nearclip; @@ -1201,8 +1201,6 @@ typedef struct KernelCamera { ProjectionTransform worldtondc; Transform worldtocamera; - DecomposedMotionTransform motion; - /* Stores changes in the projeciton matrix. Use for camera zoom motion * blur and motion pass output for perspective camera. */ ProjectionTransform perspective_pre; diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index a1c6a279956..38936ffc094 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -82,6 +82,7 @@ NODE_DEFINE(Camera) SOCKET_FLOAT(bladesrotation, "Blades Rotation", 0.0f); SOCKET_TRANSFORM(matrix, "Matrix", transform_identity()); + SOCKET_TRANSFORM_ARRAY(motion, "Motion", array()); SOCKET_FLOAT(aperture_ratio, "Aperture Ratio", 1.0f); @@ -151,9 +152,6 @@ Camera::Camera() height = 512; resolution = 1; - motion.pre = transform_identity(); - motion.post = transform_identity(); - use_motion = false; use_perspective_motion = false; shutter_curve.resize(RAMP_TABLE_SIZE); @@ -317,15 +315,22 @@ void Camera::update(Scene *scene) kcam->ndctoworld = ndctoworld; /* camera motion */ - kcam->have_motion = 0; + kcam->num_motion_steps = 0; kcam->have_perspective_motion = 0; + kernel_camera_motion.clear(); + + /* Test if any of the transforms are actually different. */ + bool have_motion = false; + for(size_t i = 0; i < motion.size(); i++) { + have_motion = have_motion || motion[i] != matrix; + } if(need_motion == Scene::MOTION_PASS) { /* TODO(sergey): Support perspective (zoom, fov) motion. */ if(type == CAMERA_PANORAMA) { - if(use_motion) { - kcam->motion_pass_pre = transform_inverse(motion.pre); - kcam->motion_pass_post = transform_inverse(motion.post); + if(have_motion) { + kcam->motion_pass_pre = transform_inverse(motion[0]); + kcam->motion_pass_post = transform_inverse(motion[motion.size()-1]); } else { kcam->motion_pass_pre = kcam->worldtocamera; @@ -333,9 +338,9 @@ void Camera::update(Scene *scene) } } else { - if(use_motion) { - kcam->perspective_pre = cameratoraster * transform_inverse(motion.pre); - kcam->perspective_post = cameratoraster * transform_inverse(motion.post); + if(have_motion) { + kcam->perspective_pre = cameratoraster * transform_inverse(motion[0]); + kcam->perspective_post = cameratoraster * transform_inverse(motion[motion.size()-1]); } else { kcam->perspective_pre = worldtoraster; @@ -344,9 +349,10 @@ void Camera::update(Scene *scene) } } else if(need_motion == Scene::MOTION_BLUR) { - if(use_motion) { - transform_motion_decompose(&kcam->motion, &motion, &matrix); - kcam->have_motion = 1; + if(have_motion) { + kernel_camera_motion.resize(motion.size()); + transform_motion_decompose(kernel_camera_motion.data(), motion.data(), motion.size()); + kcam->num_motion_steps = motion.size(); } /* TODO(sergey): Support other types of camera. */ @@ -469,6 +475,16 @@ void Camera::device_update(Device * /* device */, } dscene->data.cam = kernel_camera; + + size_t num_motion_steps = kernel_camera_motion.size(); + if(num_motion_steps) { + DecomposedTransform *camera_motion = dscene->camera_motion.alloc(num_motion_steps); + memcpy(camera_motion, kernel_camera_motion.data(), sizeof(*camera_motion) * num_motion_steps); + dscene->camera_motion.copy_to_device(); + } + else { + dscene->camera_motion.free(); + } } void Camera::device_update_volume(Device * /*device*/, @@ -495,10 +511,11 @@ void Camera::device_update_volume(Device * /*device*/, } void Camera::device_free(Device * /*device*/, - DeviceScene * /*dscene*/, + DeviceScene *dscene, Scene *scene) { scene->lookup_tables->remove_table(&shutter_table_offset); + dscene->camera_motion.free(); } bool Camera::modified(const Camera& cam) @@ -509,7 +526,6 @@ bool Camera::modified(const Camera& cam) bool Camera::motion_modified(const Camera& cam) { return !((motion == cam.motion) && - (use_motion == cam.use_motion) && (use_perspective_motion == cam.use_perspective_motion)); } @@ -706,17 +722,17 @@ float Camera::world_to_raster_size(float3 P) * may be a better way to do this, but calculating differentials from the * point directly ahead seems to produce good enough results. */ #if 0 - float2 dir = direction_to_panorama(&kernel_camera, normalize(D)); + float2 dir = direction_to_panorama(&kernel_camera, kernel_camera_motion.data(), normalize(D)); float3 raster = transform_perspective(&cameratoraster, make_float3(dir.x, dir.y, 0.0f)); ray.t = 1.0f; - camera_sample_panorama(&kernel_camera, raster.x, raster.y, 0.0f, 0.0f, &ray); + camera_sample_panorama(&kernel_camera, kernel_camera_motion.data(), raster.x, raster.y, 0.0f, 0.0f, &ray); if(ray.t == 0.0f) { /* No differentials, just use from directly ahead. */ - camera_sample_panorama(&kernel_camera, 0.5f*width, 0.5f*height, 0.0f, 0.0f, &ray); + camera_sample_panorama(&kernel_camera, kernel_camera_motion.data(), 0.5f*width, 0.5f*height, 0.0f, 0.0f, &ray); } #else - camera_sample_panorama(&kernel_camera, 0.5f*width, 0.5f*height, 0.0f, 0.0f, &ray); + camera_sample_panorama(&kernel_camera, kernel_camera_motion.data(), 0.5f*width, 0.5f*height, 0.0f, 0.0f, &ray); #endif differential_transfer(&ray.dP, ray.dP, ray.D, ray.dD, ray.D, dist); @@ -728,4 +744,27 @@ float Camera::world_to_raster_size(float3 P) return res; } +bool Camera::use_motion() const +{ + return motion.size() > 1; +} + +float Camera::motion_time(int step) const +{ + return (use_motion()) ? 2.0f * step / (motion.size() - 1) - 1.0f : 0.0f; +} + +int Camera::motion_step(float time) const +{ + if(use_motion()) { + for(int step = 0; step < motion.size(); step++) { + if(time == motion_time(step)) { + return step; + } + } + } + + return -1; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/camera.h b/intern/cycles/render/camera.h index 8f49a372f24..37d05c01bd9 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/render/camera.h @@ -141,8 +141,8 @@ public: Transform matrix; /* motion */ - MotionTransform motion; - bool use_motion, use_perspective_motion; + array motion; + bool use_perspective_motion; float fov_pre, fov_post; /* computed camera parameters */ @@ -176,6 +176,7 @@ public: /* Kernel camera data, copied here for dicing. */ KernelCamera kernel_camera; + array kernel_camera_motion; /* functions */ Camera(); @@ -199,6 +200,11 @@ public: /* Calculates the width of a pixel at point in world space. */ float world_to_raster_size(float3 P); + /* Motion blur. */ + float motion_time(int step) const; + int motion_step(float time) const; + bool use_motion() const; + private: /* Private utility functions. */ float3 transform_raster_to_world(float raster_x, float raster_y); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 6201775b992..bde340ae928 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -139,9 +139,10 @@ void Object::compute_bounds(bool motion_blur) if(mtfm.post == transform_empty()) { mtfm.post = tfm; } + mtfm.mid = tfm; DecomposedMotionTransform decomp; - transform_motion_decompose(&decomp, &mtfm, &tfm); + transform_motion_decompose(&decomp.pre, &mtfm.pre, 3); bounds = BoundBox::empty; @@ -151,7 +152,7 @@ void Object::compute_bounds(bool motion_blur) for(float t = 0.0f; t < 1.0f; t += (1.0f/128.0f)) { Transform ttfm; - transform_motion_interpolate(&ttfm, &decomp, t); + transform_motion_array_interpolate(&ttfm, &decomp.pre, 3, t); bounds.grow(mbounds.transformed(&ttfm)); } } @@ -422,8 +423,10 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s if(ob->use_motion) { /* decompose transformations for interpolation. */ DecomposedMotionTransform decomp; + MotionTransform mtfm = ob->motion; + mtfm.mid = tfm; - transform_motion_decompose(&decomp, &ob->motion, &ob->tfm); + transform_motion_decompose(&decomp.pre, &mtfm.pre, 3); kobject.tfm = decomp; flag |= SD_OBJECT_MOTION; state->have_motion = true; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index e9b9b417e30..6fc6e453aff 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -62,6 +62,7 @@ DeviceScene::DeviceScene(Device *device) objects(device, "__objects", MEM_TEXTURE), object_motion_pass(device, "__object_motion_pass", MEM_TEXTURE), object_flag(device, "__object_flag", MEM_TEXTURE), + camera_motion(device, "__camera_motion", MEM_TEXTURE), attributes_map(device, "__attributes_map", MEM_TEXTURE), attributes_float(device, "__attributes_float", MEM_TEXTURE), attributes_float3(device, "__attributes_float3", MEM_TEXTURE), diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index aebd84f92d8..74e1df2f1d2 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -90,6 +90,9 @@ public: device_vector object_motion_pass; device_vector object_flag; + /* cameras */ + device_vector camera_motion; + /* attributes */ device_vector attributes_map; device_vector attributes_float; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index e8d9558c38d..5732faf2a36 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -656,7 +656,7 @@ DeviceRequestedFeatures Session::get_requested_device_features() */ requested_features.use_hair = false; requested_features.use_object_motion = false; - requested_features.use_camera_motion = scene->camera->use_motion; + requested_features.use_camera_motion = scene->camera->use_motion(); foreach(Object *object, scene->objects) { Mesh *mesh = object->mesh; if(mesh->num_curves()) { diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/util_transform.cpp index e4e0d627497..206c3da23eb 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/util_transform.cpp @@ -261,18 +261,18 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf decomp->w = make_float4(scale.y.z, scale.z.x, scale.z.y, scale.z.z); } -void transform_motion_decompose(DecomposedMotionTransform *decomp, const MotionTransform *motion, const Transform *mid) +void transform_motion_decompose(DecomposedTransform *decomp, const Transform *motion, size_t size) { - transform_decompose(&decomp->pre, &motion->pre); - transform_decompose(&decomp->mid, mid); - transform_decompose(&decomp->post, &motion->post); + for(size_t i = 0; i < size; i++) { + transform_decompose(decomp + i, motion + i); - /* ensure rotation around shortest angle, negated quaternions are the same - * but this means we don't have to do the check in quat_interpolate */ - if(dot(decomp->pre.x, decomp->mid.x) < 0.0f) - decomp->pre.x = -decomp->pre.x; - if(dot(decomp->mid.x, decomp->post.x) < 0.0f) - decomp->mid.x = -decomp->mid.x; + if(i > 0) { + /* Ensure rotation around shortest angle, negated quaternions are the same + * but this means we don't have to do the check in quat_interpolate */ + if(dot(decomp[i-1].x, decomp[i].x) < 0.0f) + decomp[i-1].x = -decomp[i-1].x; + } + } } Transform transform_from_viewplane(BoundBox2D& viewplane) diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index 12f8405f233..ade74364876 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -409,58 +409,28 @@ ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransfo tfm->z = make_float4(dot(rotation_z, scale_x), dot(rotation_z, scale_y), dot(rotation_z, scale_z), decomp->y.z); } -ccl_device void transform_motion_interpolate(Transform *tfm, const ccl_global DecomposedMotionTransform *motion, float t) +/* Interpolate from array of decomposed transforms. */ +ccl_device void transform_motion_array_interpolate(Transform *tfm, + const ccl_global DecomposedTransform *motion, + uint numsteps, + float time) { + /* Figure out which steps we need to interpolate. */ + int maxstep = numsteps-1; + int step = min((int)(time*maxstep), maxstep-1); + float t = time*maxstep - step; + + const ccl_global DecomposedTransform *a = motion + step; + const ccl_global DecomposedTransform *b = motion + step + 1; + + /* Interpolate rotation, translation and scale. */ DecomposedTransform decomp; + decomp.x = quat_interpolate(a->x, b->x, t); + decomp.y = (1.0f - t)*a->y + t*b->y; + decomp.z = (1.0f - t)*a->z + t*b->z; + decomp.w = (1.0f - t)*a->w + t*b->w; - /* linear interpolation for rotation and scale */ - if(t < 0.5f) { - t *= 2.0f; - - decomp.x = quat_interpolate(motion->pre.x, motion->mid.x, t); - decomp.y = (1.0f - t)*motion->pre.y + t*motion->mid.y; - decomp.z = (1.0f - t)*motion->pre.z + t*motion->mid.z; - decomp.w = (1.0f - t)*motion->pre.w + t*motion->mid.w; - } - else { - t = (t - 0.5f)*2.0f; - - decomp.x = quat_interpolate(motion->mid.x, motion->post.x, t); - decomp.y = (1.0f - t)*motion->mid.y + t*motion->post.y; - decomp.z = (1.0f - t)*motion->mid.z + t*motion->post.z; - decomp.w = (1.0f - t)*motion->mid.w + t*motion->post.w; - } - - /* compose rotation, translation, scale into matrix */ - transform_compose(tfm, &decomp); -} - -ccl_device void transform_motion_interpolate_constant(Transform *tfm, ccl_constant DecomposedMotionTransform *motion, float t) -{ - /* possible optimization: is it worth it adding a check to skip scaling? - * it's probably quite uncommon to have scaling objects. or can we skip - * just shearing perhaps? */ - DecomposedTransform decomp; - - /* linear interpolation for rotation and scale */ - if(t < 0.5f) { - t *= 2.0f; - - decomp.x = quat_interpolate(motion->pre.x, motion->mid.x, t); - decomp.y = (1.0f - t)*motion->pre.y + t*motion->mid.y; - decomp.z = (1.0f - t)*motion->pre.z + t*motion->mid.z; - decomp.w = (1.0f - t)*motion->pre.w + t*motion->mid.w; - } - else { - t = (t - 0.5f)*2.0f; - - decomp.x = quat_interpolate(motion->mid.x, motion->post.x, t); - decomp.y = (1.0f - t)*motion->mid.y + t*motion->post.y; - decomp.z = (1.0f - t)*motion->mid.z + t*motion->post.z; - decomp.w = (1.0f - t)*motion->mid.w + t*motion->post.w; - } - - /* compose rotation, translation, scale into matrix */ + /* Compose rotation, translation, scale into matrix. */ transform_compose(tfm, &decomp); } @@ -479,7 +449,7 @@ ccl_device_inline bool operator==(const MotionTransform& A, const MotionTransfor } float4 transform_to_quat(const Transform& tfm); -void transform_motion_decompose(DecomposedMotionTransform *decomp, const MotionTransform *motion, const Transform *mid); +void transform_motion_decompose(DecomposedTransform *decomp, const Transform *motion, size_t size); Transform transform_from_viewplane(BoundBox2D& viewplane); #endif diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/util_vector.h index 625c19c7c46..e98e4e34181 100644 --- a/intern/cycles/util/util_vector.h +++ b/intern/cycles/util/util_vector.h @@ -215,6 +215,18 @@ public: return data_; } + T* resize(size_t newsize, const T& value) + { + size_t oldsize = size(); + resize(newsize); + + for(size_t i = oldsize; i < size(); i++) { + data_[i] = value; + } + + return data_; + } + void clear() { if(data_ != NULL) { From db333d9ea4881d9f48e3cc4b1ec59b4dafb27cc0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 8 Mar 2018 04:04:52 +0100 Subject: [PATCH 15/21] Cycles: support arbitrary number of motion blur steps for objects. --- intern/cycles/blender/addon/properties.py | 2 +- intern/cycles/blender/blender_camera.cpp | 4 +- intern/cycles/blender/blender_object.cpp | 49 +++--- intern/cycles/blender/blender_util.h | 41 ++--- intern/cycles/kernel/geom/geom_object.h | 16 +- intern/cycles/kernel/kernel_light.h | 3 +- intern/cycles/kernel/kernel_textures.h | 1 + intern/cycles/kernel/kernel_types.h | 6 +- intern/cycles/render/object.cpp | 175 +++++++++++++++------- intern/cycles/render/object.h | 10 +- intern/cycles/render/scene.cpp | 1 + intern/cycles/render/scene.h | 1 + intern/cycles/render/session.cpp | 2 +- intern/cycles/util/util_transform.h | 17 --- 14 files changed, 188 insertions(+), 140 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 62aea8790af..1b95c365616 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1088,7 +1088,7 @@ class CyclesObjectSettings(bpy.types.PropertyGroup): cls.motion_steps = IntProperty( name="Motion Steps", - description="Control accuracy of deformation motion blur, more steps gives more memory usage (actual number of steps is 2^(steps - 1))", + description="Control accuracy of motion blur, more steps gives more memory usage (actual number of steps is 2^(steps - 1))", min=1, soft_max=8, default=1, ) diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 909db8d9449..f00ade320e7 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -229,9 +229,7 @@ static void blender_camera_from_object(BlenderCamera *bcam, else bcam->sensor_fit = BlenderCamera::VERTICAL; - if(object_use_motion(b_ob, b_ob)) { - bcam->motion_steps = object_motion_steps(b_ob); - } + bcam->motion_steps = object_motion_steps(b_ob, b_ob); } else { /* from lamp not implemented yet */ diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 45309292f0b..4b40f4d458b 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -327,22 +327,11 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, if(motion) { object = object_map.find(key); - if(object && (scene->need_motion() == Scene::MOTION_PASS || - object_use_motion(b_parent, b_ob))) - { - /* object transformation */ - if(tfm != object->tfm) { - VLOG(1) << "Object " << b_ob.name() << " motion detected."; - if(motion_time == -1.0f || motion_time == 1.0f) { - object->use_motion = true; - } - } - - if(motion_time == -1.0f) { - object->motion.pre = tfm; - } - else if(motion_time == 1.0f) { - object->motion.post = tfm; + if(object && object->use_motion()) { + /* Set transform at matching motion time step. */ + int time_index = object->motion_step(motion_time); + if(time_index >= 0) { + object->motion[time_index] = tfm; } /* mesh deformation */ @@ -389,24 +378,34 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, object->name = b_ob.name().c_str(); object->pass_id = b_ob.pass_index(); object->tfm = tfm; - object->motion.pre = transform_empty(); - object->motion.post = transform_empty(); - object->use_motion = false; + object->motion.clear(); /* motion blur */ - if(scene->need_motion() == Scene::MOTION_BLUR && object->mesh) { + Scene::MotionType need_motion = scene->need_motion(); + if(need_motion != Scene::MOTION_NONE && object->mesh) { Mesh *mesh = object->mesh; mesh->use_motion_blur = false; + mesh->motion_steps = 0; - if(object_use_motion(b_parent, b_ob)) { + uint motion_steps; + + if(scene->need_motion() == Scene::MOTION_BLUR) { + motion_steps = object_motion_steps(b_parent, b_ob); if(object_use_deform_motion(b_parent, b_ob)) { - mesh->motion_steps = object_motion_steps(b_ob); + mesh->motion_steps = motion_steps; mesh->use_motion_blur = true; } + } + else { + motion_steps = 3; + mesh->motion_steps = motion_steps; + } - for(size_t step = 0; step < mesh->motion_steps - 1; step++) { - motion_times.insert(mesh->motion_time(step)); - } + object->motion.resize(motion_steps, transform_empty()); + object->motion[motion_steps/2] = tfm; + + for(size_t step = 0; step < motion_steps; step++) { + motion_times.insert(object->motion_time(step)); } } diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index c4c45035b5a..c418b19a637 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -484,33 +484,34 @@ static inline void mesh_texture_space(BL::Mesh& b_mesh, loc = loc*size - make_float3(0.5f, 0.5f, 0.5f); } -/* object used for motion blur */ -static inline bool object_use_motion(BL::Object& b_parent, BL::Object& b_ob) +/* Object motion steps, returns 0 if no motion blur needed. */ +static inline uint object_motion_steps(BL::Object& b_parent, BL::Object& b_ob) { + /* Get motion enabled and steps from object itself. */ PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles"); bool use_motion = get_boolean(cobject, "use_motion_blur"); - /* If motion blur is enabled for the object we also check - * whether it's enabled for the parent object as well. - * - * This way we can control motion blur from the dupligroup - * duplicator much easier. - */ - if(use_motion && b_parent.ptr.data != b_ob.ptr.data) { + if(!use_motion) { + return 0; + } + + uint steps = max(1, get_int(cobject, "motion_steps")); + + /* Also check parent object, so motion blur and steps can be + * controlled by dupligroup duplicator for linked groups. */ + if(b_parent.ptr.data != b_ob.ptr.data) { PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles"); use_motion &= get_boolean(parent_cobject, "use_motion_blur"); + + if(!use_motion) { + return 0; + } + + steps = max(steps, get_int(parent_cobject, "motion_steps")); } - return use_motion; -} -/* object motion steps */ -static inline uint object_motion_steps(BL::Object& b_ob) -{ - PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles"); - uint steps = get_int(cobject, "motion_steps"); - - /* use uneven number of steps so we get one keyframe at the current frame, - * and ue 2^(steps - 1) so objects with more/fewer steps still have samples - * at the same times, to avoid sampling at many different times */ + /* Use uneven number of steps so we get one keyframe at the current frame, + * and use 2^(steps - 1) so objects with more/fewer steps still have samples + * at the same times, to avoid sampling at many different times. */ return (2 << (steps - 1)) + 1; } diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 67d039e1bc7..800649abf38 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -40,18 +40,12 @@ enum ObjectVectorTransform { ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object, enum ObjectTransform type) { - Transform tfm; if(type == OBJECT_INVERSE_TRANSFORM) { - tfm.x = kernel_tex_fetch(__objects, object).tfm.mid.x; - tfm.y = kernel_tex_fetch(__objects, object).tfm.mid.y; - tfm.z = kernel_tex_fetch(__objects, object).tfm.mid.z; + return kernel_tex_fetch(__objects, object).itfm; } else { - tfm.x = kernel_tex_fetch(__objects, object).tfm.pre.x; - tfm.y = kernel_tex_fetch(__objects, object).tfm.pre.y; - tfm.z = kernel_tex_fetch(__objects, object).tfm.pre.z; + return kernel_tex_fetch(__objects, object).tfm; } - return tfm; } /* Lamp to world space transformation */ @@ -79,10 +73,12 @@ ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals *kg #ifdef __OBJECT_MOTION__ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int object, float time) { - const ccl_global DecomposedMotionTransform *motion = &kernel_tex_fetch(__objects, object).tfm; + const uint motion_offset = kernel_tex_fetch(__objects, object).motion_offset; + const ccl_global DecomposedTransform *motion = &kernel_tex_fetch(__object_motion, motion_offset); + const uint num_steps = kernel_tex_fetch(__objects, object).numsteps * 2 + 1; Transform tfm; - transform_motion_array_interpolate(&tfm, &motion->pre, 3, time); + transform_motion_array_interpolate(&tfm, motion, num_steps, time); return tfm; } diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index aaf7a7abdd4..efab69ee37d 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -792,7 +792,8 @@ ccl_device_inline bool triangle_world_space_vertices(KernelGlobals *kg, int obje #ifdef __INSTANCING__ if(!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { # ifdef __OBJECT_MOTION__ - Transform tfm = object_fetch_transform_motion_test(kg, object, time, NULL); + float object_time = (time >= 0.0f) ? time : 0.5f; + Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL); # else Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); # endif diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 56de5b27605..9047b93a0b2 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -33,6 +33,7 @@ KERNEL_TEX(float2, __prim_time) /* objects */ KERNEL_TEX(KernelObject, __objects) KERNEL_TEX(Transform, __object_motion_pass) +KERNEL_TEX(DecomposedTransform, __object_motion) KERNEL_TEX(uint, __object_flag) /* cameras */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 2c3b7ba29a4..977ceac12ea 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1433,7 +1433,8 @@ static_assert_align(KernelData, 16); /* Kernel data structures. */ typedef struct KernelObject { - DecomposedMotionTransform tfm; + Transform tfm; + Transform itfm; float surface_area; float pass_id; @@ -1449,7 +1450,8 @@ typedef struct KernelObject { uint patch_map_offset; uint attribute_map_offset; - uint pad1, pad2; + uint motion_offset; + uint pad; } KernelObject;; static_assert_align(KernelObject, 16); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index bde340ae928..138de250c5f 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -54,6 +54,9 @@ struct UpdateObjectTransformState { */ map surface_area_map; + /* Motion offsets for each object. */ + array motion_offset; + /* Packed object arrays. Those will be filled in. */ uint *object_flag; KernelObject *objects; @@ -91,6 +94,7 @@ NODE_DEFINE(Object) SOCKET_BOOLEAN(hide_on_missing_motion, "Hide on Missing Motion", false); SOCKET_POINT(dupli_generated, "Dupli Generated", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_POINT2(dupli_uv, "Dupli UV", make_float2(0.0f, 0.0f)); + SOCKET_TRANSFORM_ARRAY(motion, "Motion", array()); SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", false); @@ -103,46 +107,54 @@ Object::Object() particle_system = NULL; particle_index = 0; bounds = BoundBox::empty; - motion.pre = transform_empty(); - motion.mid = transform_empty(); - motion.post = transform_empty(); - use_motion = false; } Object::~Object() { } +void Object::update_motion() +{ + if(!use_motion()) { + return; + } + + bool have_motion = false; + + for(size_t i = 0; i < motion.size(); i++) { + if(motion[i] == transform_empty()) { + if(hide_on_missing_motion) { + /* Hide objects that have no valid previous or next + * transform, for example particle that stop existing. It + * would be better to handle this in the kernel and make + * objects invisible outside certain motion steps. */ + tfm = transform_empty(); + motion.clear(); + return; + } + else { + /* Otherwise just copy center motion. */ + motion[i] = tfm; + } + } + + /* Test if any of the transforms are actually different. */ + have_motion = have_motion || motion[i] != tfm; + } + + /* Clear motion array if there is no actual motion. */ + if(!have_motion) { + motion.clear(); + } +} + void Object::compute_bounds(bool motion_blur) { BoundBox mbounds = mesh->bounds; - if(motion_blur && use_motion) { - MotionTransform mtfm = motion; - - if(hide_on_missing_motion) { - /* Hide objects that have no valid previous or next transform, for - * example particle that stop existing. TODO: add support for this - * case in the kernel so we don't get render artifacts. */ - if(mtfm.pre == transform_empty() || - mtfm.post == transform_empty()) { - bounds = BoundBox::empty; - return; - } - } - - /* In case of missing motion information for previous/next frame, - * assume there is no motion. */ - if(mtfm.pre == transform_empty()) { - mtfm.pre = tfm; - } - if(mtfm.post == transform_empty()) { - mtfm.post = tfm; - } - mtfm.mid = tfm; - - DecomposedMotionTransform decomp; - transform_motion_decompose(&decomp.pre, &mtfm.pre, 3); + if(motion_blur && use_motion()) { + array decomp(motion.size()); + transform_motion_decompose(decomp.data(), motion.data(), motion.size()); bounds = BoundBox::empty; @@ -152,11 +164,12 @@ void Object::compute_bounds(bool motion_blur) for(float t = 0.0f; t < 1.0f; t += (1.0f/128.0f)) { Transform ttfm; - transform_motion_array_interpolate(&ttfm, &decomp.pre, 3, t); + transform_motion_array_interpolate(&ttfm, decomp.data(), motion.size(), t); bounds.grow(mbounds.transformed(&ttfm)); } } else { + /* No motion blur case. */ if(mesh->transform_applied) { bounds = mbounds; } @@ -276,6 +289,29 @@ void Object::tag_update(Scene *scene) scene->object_manager->need_update = true; } +bool Object::use_motion() const +{ + return (motion.size() > 1); +} + +float Object::motion_time(int step) const +{ + return (use_motion()) ? 2.0f * step / (motion.size() - 1) - 1.0f : 0.0f; +} + +int Object::motion_step(float time) const +{ + if(use_motion()) { + for(size_t step = 0; step < motion.size(); step++) { + if(time == motion_time(step)) { + return step; + } + } + } + + return -1; +} + bool Object::is_traceable() const { /* Mesh itself can be empty,can skip all such objects. */ @@ -381,12 +417,13 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s } } - memcpy(&kobject.tfm.pre, &tfm, sizeof(tfm)); - memcpy(&kobject.tfm.mid, &itfm, sizeof(itfm)); + kobject.tfm = tfm; + kobject.itfm = itfm; kobject.surface_area = surface_area; kobject.pass_id = pass_id; kobject.random_number = random_number; kobject.particle_index = particle_index; + kobject.motion_offset = 0; if(mesh->use_motion_blur) { state->have_motion = true; @@ -396,38 +433,39 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s } if(state->need_motion == Scene::MOTION_PASS) { + /* Clear motion array if there is no actual motion. */ + ob->update_motion(); + + /* Compute motion transforms. */ + Transform tfm_pre, tfm_post; + if(ob->use_motion()) { + tfm_pre = ob->motion[0]; + tfm_post = ob->motion[ob->motion.size() - 1]; + } + else { + tfm_pre = tfm; + tfm_post = tfm; + } + /* Motion transformations, is world/object space depending if mesh * comes with deformed position in object space, or if we transform - * the shading point in world space. - */ - MotionTransform mtfm = ob->motion; - - /* In case of missing motion information for previous/next frame, - * assume there is no motion. */ - if(!ob->use_motion || mtfm.pre == transform_empty()) { - mtfm.pre = ob->tfm; - } - if(!ob->use_motion || mtfm.post == transform_empty()) { - mtfm.post = ob->tfm; - } - + * the shading point in world space. */ if(!mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) { - mtfm.pre = mtfm.pre * itfm; - mtfm.post = mtfm.post * itfm; + tfm_pre = tfm_pre * itfm; + tfm_post = tfm_post * itfm; } - object_motion_pass[object_index*OBJECT_MOTION_PASS_SIZE+0] = mtfm.pre; - object_motion_pass[object_index*OBJECT_MOTION_PASS_SIZE+1] = mtfm.post; + int motion_pass_offset = object_index*OBJECT_MOTION_PASS_SIZE; + object_motion_pass[motion_pass_offset + 0] = tfm_pre; + object_motion_pass[motion_pass_offset + 1] = tfm_post; } else if(state->need_motion == Scene::MOTION_BLUR) { - if(ob->use_motion) { - /* decompose transformations for interpolation. */ - DecomposedMotionTransform decomp; - MotionTransform mtfm = ob->motion; - mtfm.mid = tfm; + if(ob->use_motion()) { + kobject.motion_offset = state->motion_offset[object_index]; - transform_motion_decompose(&decomp.pre, &mtfm.pre, 3); - kobject.tfm = decomp; + /* Decompose transforms for interpolation. */ + DecomposedTransform *decomp = state->object_motion + kobject.motion_offset; + transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size()); flag |= SD_OBJECT_MOTION; state->have_motion = true; } @@ -512,11 +550,28 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, state.objects = dscene->objects.alloc(scene->objects.size()); state.object_flag = dscene->object_flag.alloc(scene->objects.size()); + state.object_motion = NULL; state.object_motion_pass = NULL; if(state.need_motion == Scene::MOTION_PASS) { state.object_motion_pass = dscene->object_motion_pass.alloc(OBJECT_MOTION_PASS_SIZE*scene->objects.size()); } + else if(state.need_motion == Scene::MOTION_BLUR) { + /* Set object offsets into global object motion array. */ + uint *motion_offsets = state.motion_offset.resize(scene->objects.size()); + uint motion_offset = 0; + + foreach(Object *ob, scene->objects) { + *motion_offsets = motion_offset; + motion_offsets++; + + /* Clear motion array if there is no actual motion. */ + ob->update_motion(); + motion_offset += ob->motion.size(); + } + + state.object_motion = dscene->object_motion.alloc(motion_offset); + } /* Particle system device offsets * 0 is dummy particle, index starts at 1. @@ -560,6 +615,9 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, if(state.need_motion == Scene::MOTION_PASS) { dscene->object_motion_pass.copy_to_device(); } + else if(state.need_motion == Scene::MOTION_BLUR) { + dscene->object_motion.copy_to_device(); + } dscene->data.bvh.have_motion = state.have_motion; dscene->data.bvh.have_curves = state.have_curves; @@ -709,6 +767,7 @@ void ObjectManager::device_free(Device *, DeviceScene *dscene) { dscene->objects.free(); dscene->object_motion_pass.free(); + dscene->object_motion.free(); dscene->object_flag.free(); } @@ -748,7 +807,7 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P if((mesh_users[object->mesh] == 1 && !object->mesh->has_surface_bssrdf) && !object->mesh->has_true_displacement() && object->mesh->subdivision_type == Mesh::SUBDIVISION_NONE) { - if(!(motion_blur && object->use_motion)) { + if(!(motion_blur && object->use_motion())) { if(!object->mesh->transform_applied) { object->apply_transform(apply_to_motion); object->mesh->transform_applied = true; diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 7cf1528c4a7..c7212ae25f9 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -50,8 +50,7 @@ public: int pass_id; vector attributes; uint visibility; - MotionTransform motion; - bool use_motion; + array motion; bool hide_on_missing_motion; bool use_holdout; bool is_shadow_catcher; @@ -70,6 +69,13 @@ public: void compute_bounds(bool motion_blur); void apply_transform(bool apply_to_motion); + /* Convert between normalized -1..1 motion time and index + * in the motion array. */ + bool use_motion() const; + float motion_time(int step) const; + int motion_step(float time) const; + void update_motion(); + /* Check whether object is traceable and it worth adding it to * kernel scene. */ diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 6fc6e453aff..ba47e3ab6f8 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -61,6 +61,7 @@ DeviceScene::DeviceScene(Device *device) patches(device, "__patches", MEM_TEXTURE), objects(device, "__objects", MEM_TEXTURE), object_motion_pass(device, "__object_motion_pass", MEM_TEXTURE), + object_motion(device, "__object_motion", MEM_TEXTURE), object_flag(device, "__object_flag", MEM_TEXTURE), camera_motion(device, "__camera_motion", MEM_TEXTURE), attributes_map(device, "__attributes_map", MEM_TEXTURE), diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 74e1df2f1d2..04bd4735a86 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -88,6 +88,7 @@ public: /* objects */ device_vector objects; device_vector object_motion_pass; + device_vector object_motion; device_vector object_flag; /* cameras */ diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 5732faf2a36..41156038558 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -662,7 +662,7 @@ DeviceRequestedFeatures Session::get_requested_device_features() if(mesh->num_curves()) { requested_features.use_hair = true; } - requested_features.use_object_motion |= object->use_motion | mesh->use_motion_blur; + requested_features.use_object_motion |= object->use_motion() | mesh->use_motion_blur; requested_features.use_camera_motion |= mesh->use_motion_blur; #ifdef WITH_OPENSUBDIV if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE) { diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index ade74364876..987f4dac777 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -37,12 +37,6 @@ typedef struct Transform { #endif } Transform; -typedef struct ccl_may_alias MotionTransform { - Transform pre; - Transform mid; - Transform post; -} MotionTransform; - /* Transform decomposed in rotation/translation/scale. we use the same data * structure as Transform, and tightly pack decomposition into it. first the * rotation (4), then translation (3), then 3x3 scale matrix (9). */ @@ -51,12 +45,6 @@ typedef struct DecomposedTransform { float4 x, y, z, w; } DecomposedTransform; -typedef struct ccl_may_alias DecomposedMotionTransform { - DecomposedTransform pre; - DecomposedTransform mid; - DecomposedTransform post; -} DecomposedMotionTransform; - /* Functions */ ccl_device_inline float3 transform_point(const Transform *t, const float3 a) @@ -443,11 +431,6 @@ ccl_device_inline bool operator==(const DecomposedTransform& A, const Decomposed return memcmp(&A, &B, sizeof(DecomposedTransform)) == 0; } -ccl_device_inline bool operator==(const MotionTransform& A, const MotionTransform& B) -{ - return (A.pre == B.pre && A.post == B.post); -} - float4 transform_to_quat(const Transform& tfm); void transform_motion_decompose(DecomposedTransform *decomp, const Transform *motion, size_t size); Transform transform_from_viewplane(BoundBox2D& viewplane); From d526b35ce282b97bef99844f405e802788a2d399 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 03:57:18 +0100 Subject: [PATCH 16/21] Code cleanup: use spaces for aligned indentation. --- intern/cycles/render/mesh_volume.cpp | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp index f2347c79610..3571beb40d6 100644 --- a/intern/cycles/render/mesh_volume.cpp +++ b/intern/cycles/render/mesh_volume.cpp @@ -152,22 +152,22 @@ public: void add_node_with_padding(int x, int y, int z); void create_mesh(vector &vertices, - vector &indices, - vector &face_normals); + vector &indices, + vector &face_normals); private: void generate_vertices_and_quads(vector &vertices_is, - vector &quads); + vector &quads); void deduplicate_vertices(vector &vertices, - vector &quads); + vector &quads); void convert_object_space(const vector &vertices, - vector &out_vertices); + vector &out_vertices); void convert_quads_to_tris(const vector &quads, - vector &tris, - vector &face_normals); + vector &tris, + vector &face_normals); }; VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params) @@ -224,8 +224,8 @@ void VolumeMeshBuilder::add_node_with_padding(int x, int y, int z) } void VolumeMeshBuilder::create_mesh(vector &vertices, - vector &indices, - vector &face_normals) + vector &indices, + vector &face_normals) { /* We create vertices in index space (is), and only convert them to object * space when done. */ @@ -260,8 +260,8 @@ void VolumeMeshBuilder::generate_vertices_and_quads( /* Compute min and max coords of the node in index space. */ int3 min = make_int3((x - pad_offset.x)*CUBE_SIZE, - (y - pad_offset.y)*CUBE_SIZE, - (z - pad_offset.z)*CUBE_SIZE); + (y - pad_offset.y)*CUBE_SIZE, + (z - pad_offset.z)*CUBE_SIZE); /* Maximum is just CUBE_SIZE voxels away from minimum on each axis. */ int3 max = make_int3(min.x + CUBE_SIZE, min.y + CUBE_SIZE, min.z + CUBE_SIZE); @@ -316,7 +316,7 @@ void VolumeMeshBuilder::generate_vertices_and_quads( } void VolumeMeshBuilder::deduplicate_vertices(vector &vertices, - vector &quads) + vector &quads) { vector sorted_vertices = vertices; std::sort(sorted_vertices.begin(), sorted_vertices.end()); @@ -355,7 +355,7 @@ void VolumeMeshBuilder::deduplicate_vertices(vector &vertices, } void VolumeMeshBuilder::convert_object_space(const vector &vertices, - vector &out_vertices) + vector &out_vertices) { out_vertices.reserve(vertices.size()); @@ -369,8 +369,8 @@ void VolumeMeshBuilder::convert_object_space(const vector &vertices, } void VolumeMeshBuilder::convert_quads_to_tris(const vector &quads, - vector &tris, - vector &face_normals) + vector &tris, + vector &face_normals) { int index_offset = 0; tris.resize(quads.size()*6); @@ -399,8 +399,8 @@ struct VoxelAttributeGrid { }; void MeshManager::create_volume_mesh(Scene *scene, - Mesh *mesh, - Progress& progress) + Mesh *mesh, + Progress& progress) { string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str()); progress.set_status("Updating Mesh", msg); @@ -470,8 +470,8 @@ void MeshManager::create_volume_mesh(Scene *scene, const int3 resolution = volume_params.resolution; float3 start_point = make_float3(0.0f, 0.0f, 0.0f); float3 cell_size = make_float3(1.0f/resolution.x, - 1.0f/resolution.y, - 1.0f/resolution.z); + 1.0f/resolution.y, + 1.0f/resolution.z); if(attr) { const Transform *tfm = attr->data_transform(); @@ -575,12 +575,12 @@ void MeshManager::create_volume_mesh(Scene *scene, /* Print stats. */ VLOG(1) << "Memory usage volume mesh: " - << ((vertices.size() + face_normals.size())*sizeof(float3) + indices.size()*sizeof(int))/(1024.0*1024.0) - << "Mb."; + << ((vertices.size() + face_normals.size())*sizeof(float3) + indices.size()*sizeof(int))/(1024.0*1024.0) + << "Mb."; VLOG(1) << "Memory usage volume grid: " - << (resolution.x*resolution.y*resolution.z*sizeof(float))/(1024.0*1024.0) - << "Mb."; + << (resolution.x*resolution.y*resolution.z*sizeof(float))/(1024.0*1024.0) + << "Mb."; } CCL_NAMESPACE_END From 50dde3d01ac5076cf42a0523d9eab4aac917a990 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 04:33:14 +0100 Subject: [PATCH 17/21] Fix T54269: saved EXR file files unreadable some editors in Blender. Don't write the multichannel metadata when there is only a single layer, and don't unnecessarily consider single layer images with Blender metadata as multi layer. --- source/blender/imbuf/intern/openexr/openexr_api.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 4cb1c13a44a..4e85d70d382 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1621,14 +1621,13 @@ static bool exr_has_alpha(MultiPartInputFile& file) static bool imb_exr_is_multilayer_file(MultiPartInputFile& file) { - const StringAttribute *comments = file.header(0).findTypedAttribute("BlenderMultiChannel"); const ChannelList& channels = file.header(0).channels(); std::set layerNames; /* will not include empty layer names */ channels.layers(layerNames); - if (comments || layerNames.size() > 1) + if (layerNames.size() > 1) return true; if (layerNames.size()) { @@ -1667,7 +1666,7 @@ static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, } else { *r_singlelayer = false; - *r_multilayer = true; + *r_multilayer = (layerNames.size() > 1); *r_multiview = false; return; } From 07d0a87b5c20dfc4a404f2354325b91ff21653b2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 05:03:03 +0100 Subject: [PATCH 18/21] Fix T54278: volume artifacts with small density and color after recent changes. --- intern/cycles/kernel/geom/geom_volume.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index 286d898992e..a4e47384b25 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -68,7 +68,7 @@ ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *s if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f); if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f); - if(r.w != 0.0f && r.w != 1.0f) { + if(r.w > 1e-8f && r.w != 1.0f) { /* For RGBA colors, unpremultiply after interpolation. */ return float4_to_float3(r) / r.w; } From 3f2b88d195a1ac557e2b4782a31e19cd0df0517d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 05:45:49 +0100 Subject: [PATCH 19/21] Code refactor: reduce number of loops over meshes. --- intern/cycles/render/mesh.cpp | 97 +++++++++++++++++------------------ 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 9166d3bcd5e..d6c37c22099 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -2057,7 +2057,9 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen VLOG(1) << "Total " << scene->meshes.size() << " meshes."; - /* Update normals. */ + bool true_displacement_used = false; + size_t total_tess_needed = 0; + foreach(Mesh *mesh, scene->meshes) { foreach(Shader *shader, mesh->used_shaders) { if(shader->need_update_mesh) @@ -2065,6 +2067,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen } if(mesh->need_update) { + /* Update normals. */ mesh->add_face_normals(); mesh->add_vertex_normals(); @@ -2072,57 +2075,53 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen mesh->add_undisplaced(); } + /* Test if we need tesselation. */ + if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE && + mesh->num_subd_verts == 0 && + mesh->subd_params) + { + total_tess_needed++; + } + + /* Test if we need displacement. */ + if(mesh->has_true_displacement()) { + true_displacement_used = true; + } + if(progress.get_cancel()) return; } } /* Tessellate meshes that are using subdivision */ - size_t total_tess_needed = 0; - foreach(Mesh *mesh, scene->meshes) { - if(mesh->need_update && - mesh->subdivision_type != Mesh::SUBDIVISION_NONE && - mesh->num_subd_verts == 0 && - mesh->subd_params) - { - total_tess_needed++; - } - } + if(total_tess_needed) { + size_t i = 0; + foreach(Mesh *mesh, scene->meshes) { + if(mesh->need_update && + mesh->subdivision_type != Mesh::SUBDIVISION_NONE && + mesh->num_subd_verts == 0 && + mesh->subd_params) + { + string msg = "Tessellating "; + if(mesh->name == "") + msg += string_printf("%u/%u", (uint)(i+1), (uint)total_tess_needed); + else + msg += string_printf("%s %u/%u", mesh->name.c_str(), (uint)(i+1), (uint)total_tess_needed); - size_t i = 0; - foreach(Mesh *mesh, scene->meshes) { - if(mesh->need_update && - mesh->subdivision_type != Mesh::SUBDIVISION_NONE && - mesh->num_subd_verts == 0 && - mesh->subd_params) - { - string msg = "Tessellating "; - if(mesh->name == "") - msg += string_printf("%u/%u", (uint)(i+1), (uint)total_tess_needed); - else - msg += string_printf("%s %u/%u", mesh->name.c_str(), (uint)(i+1), (uint)total_tess_needed); + progress.set_status("Updating Mesh", msg); - progress.set_status("Updating Mesh", msg); + DiagSplit dsplit(*mesh->subd_params); + mesh->tessellate(&dsplit); - DiagSplit dsplit(*mesh->subd_params); - mesh->tessellate(&dsplit); + i++; - i++; + if(progress.get_cancel()) return; + } - if(progress.get_cancel()) return; } } /* Update images needed for true displacement. */ - bool true_displacement_used = false; bool old_need_object_flags_update = false; - foreach(Mesh *mesh, scene->meshes) { - if(mesh->need_update && - mesh->has_true_displacement()) - { - true_displacement_used = true; - break; - } - } if(true_displacement_used) { VLOG(1) << "Updating images used for true displacement."; device_update_displacement_images(device, scene, progress); @@ -2148,11 +2147,17 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen /* Update displacement. */ bool displacement_done = false; + size_t num_bvh = 0; + foreach(Mesh *mesh, scene->meshes) { - if(mesh->need_update && - displace(device, dscene, scene, mesh, progress)) - { - displacement_done = true; + if(mesh->need_update) { + if(displace(device, dscene, scene, mesh, progress)) { + displacement_done = true; + } + + if(mesh->need_build_bvh()) { + num_bvh++; + } } } @@ -2167,17 +2172,9 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen if(progress.get_cancel()) return; } - /* Update bvh. */ - size_t num_bvh = 0; - foreach(Mesh *mesh, scene->meshes) { - if(mesh->need_update && mesh->need_build_bvh()) { - num_bvh++; - } - } - TaskPool pool; - i = 0; + size_t i = 0; foreach(Mesh *mesh, scene->meshes) { if(mesh->need_update) { pool.push(function_bind(&Mesh::compute_bvh, From 1f5c5fa189db8d5d505246dc7e905a796d21ce22 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 06:10:14 +0100 Subject: [PATCH 20/21] Fix T54279: viewport update bug with volume meshes. Meshes without vertex normals were not handled correctly. --- intern/cycles/render/mesh.cpp | 29 ++++++++++++++++------------- intern/cycles/render/mesh.h | 3 ++- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index d6c37c22099..7d5a0e451d0 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -877,15 +877,8 @@ void Mesh::add_undisplaced() } } -void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal) +void Mesh::pack_shaders(Scene *scene, uint *tri_shader) { - Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL); - if(attr_vN == NULL) { - /* Happens on objects with just hair. */ - return; - } - - float3 *vN = attr_vN->data_float3(); uint shader_id = 0; uint last_shader = -1; bool last_smooth = false; @@ -893,10 +886,6 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal) size_t triangles_size = num_triangles(); int *shader_ptr = shader.data(); - bool do_transform = transform_applied; - Transform ntfm = transform_normal; - - /* save shader */ for(size_t i = 0; i < triangles_size; i++) { if(shader_ptr[i] != last_shader || last_smooth != smooth[i]) { last_shader = shader_ptr[i]; @@ -908,7 +897,20 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal) tri_shader[i] = shader_id; } +} +void Mesh::pack_normals(Scene *scene, float4 *vnormal) +{ + Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL); + if(attr_vN == NULL) { + /* Happens on objects with just hair. */ + return; + } + + bool do_transform = transform_applied; + Transform ntfm = transform_normal; + + float3 *vN = attr_vN->data_float3(); size_t verts_size = verts.size(); for(size_t i = 0; i < verts_size; i++) { @@ -1773,8 +1775,9 @@ void MeshManager::device_update_mesh(Device *, float2 *tri_patch_uv = dscene->tri_patch_uv.alloc(vert_size); foreach(Mesh *mesh, scene->meshes) { + mesh->pack_shaders(scene, + &tri_shader[mesh->tri_offset]); mesh->pack_normals(scene, - &tri_shader[mesh->tri_offset], &vnormal[mesh->vert_offset]); mesh->pack_verts(tri_prim_index, &tri_vindex[mesh->tri_offset], diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 6042faccad5..03a419e77f2 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -279,7 +279,8 @@ public: void add_vertex_normals(); void add_undisplaced(); - void pack_normals(Scene *scene, uint *shader, float4 *vnormal); + void pack_shaders(Scene *scene, uint *shader); + void pack_normals(Scene *scene, float4 *vnormal); void pack_verts(const vector& tri_prim_index, uint4 *tri_vindex, uint *tri_patch, From 8a76f8dac3475b1d24956e0d384d65295f15c76a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 10 Mar 2018 06:51:52 +0100 Subject: [PATCH 21/21] Code cleanup: fix compiler warning. --- intern/cycles/render/mesh.cpp | 5 ++--- intern/cycles/render/mesh.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 7d5a0e451d0..7cfbb7b7c7d 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -899,7 +899,7 @@ void Mesh::pack_shaders(Scene *scene, uint *tri_shader) } } -void Mesh::pack_normals(Scene *scene, float4 *vnormal) +void Mesh::pack_normals(float4 *vnormal) { Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL); if(attr_vN == NULL) { @@ -1777,8 +1777,7 @@ void MeshManager::device_update_mesh(Device *, foreach(Mesh *mesh, scene->meshes) { mesh->pack_shaders(scene, &tri_shader[mesh->tri_offset]); - mesh->pack_normals(scene, - &vnormal[mesh->vert_offset]); + mesh->pack_normals(&vnormal[mesh->vert_offset]); mesh->pack_verts(tri_prim_index, &tri_vindex[mesh->tri_offset], &tri_patch[mesh->tri_offset], diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 03a419e77f2..e370f8a2021 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -280,7 +280,7 @@ public: void add_undisplaced(); void pack_shaders(Scene *scene, uint *shader); - void pack_normals(Scene *scene, float4 *vnormal); + void pack_normals(float4 *vnormal); void pack_verts(const vector& tri_prim_index, uint4 *tri_vindex, uint *tri_patch,