Compare commits
109 Commits
temp-inter
...
temp-sampl
Author | SHA1 | Date | |
---|---|---|---|
b5cb2d50f4 | |||
6ee633575f | |||
32663fc287 | |||
8c40473286 | |||
7e82c840b7 | |||
2549384baa | |||
73047c69ea | |||
e8a8bb67fc | |||
ab9ec193c3 | |||
![]() |
c4ea5cb1a3 | ||
1143bf281a | |||
acc800d24d | |||
dc378bf1a4 | |||
0a6f428be7 | |||
60b8eb30bb | |||
eed48a7322 | |||
738f4fbc5e | |||
ebb4aba325 | |||
6b4ca78108 | |||
ec432ae998 | |||
8d3a771574 | |||
55c69373e8 | |||
8b13cf5667 | |||
5941c39fbf | |||
30f9034182 | |||
e5a7dd8ab6 | |||
5b787c24fb | |||
888b879f5f | |||
1b55b911f2 | |||
71131b4969 | |||
b4d9b8b7f8 | |||
ef0b8d6306 | |||
d845ba481c | |||
cbca71a7cf | |||
809ae823b7 | |||
9d0d4b8601 | |||
8a8bf99717 | |||
2b394e1108 | |||
![]() |
a89529d8db | ||
9f5290e3bc | |||
c671b5eee4 | |||
![]() |
896d3f1ce5 | ||
76105eb752 | |||
d48523cb4d | |||
ddf66cd060 | |||
b4cfe80547 | |||
c7a88cf91a | |||
5f7d5c0809 | |||
a470e3c9d1 | |||
0ea60cf6b8 | |||
3fe735d371 | |||
a47359ff36 | |||
de8a46c6ad | |||
26502f3d89 | |||
2b633f12ad | |||
86ca206db8 | |||
![]() |
456876208b | ||
![]() |
32c7687859 | ||
a87253942d | |||
ae74ad191c | |||
aa1c44a113 | |||
5c0d4753cf | |||
02333544d1 | |||
1061f5a1ba | |||
1a7757b0bc | |||
f133c6b094 | |||
d612d92630 | |||
1e1c870001 | |||
0533f2851e | |||
d6e682a7b0 | |||
bd734cc441 | |||
e61da8e4fb | |||
f3bdabbe24 | |||
50f32025ac | |||
393879f30c | |||
3d9c8397fc | |||
c9c7658926 | |||
3ca41b7312 | |||
![]() |
4bc08b79aa | ||
25e7365d0d | |||
52c617802f | |||
![]() |
7aa39b40f4 | ||
ce395c84a3 | |||
d26d3cfe19 | |||
06a74e7816 | |||
9f31b9b7d3 | |||
9be6880d02 | |||
4a98faf9f1 | |||
8c240f50b2 | |||
b7e2408ea4 | |||
bb64155c63 | |||
![]() |
c63e735f6b | ||
b8d53b703a | |||
9787b46f09 | |||
03f0be35d6 | |||
d753ebd40a | |||
ddf0bacaa9 | |||
3929db265f | |||
e1bd4bbb66 | |||
f1a8644121 | |||
1ec7075ff2 | |||
22ffd69a91 | |||
9ca8bf0b29 | |||
040630bb9a | |||
3fa86f4b28 | |||
7689f501e2 | |||
![]() |
e507a789b3 | ||
6b0008129e | |||
c8e93da0a7 |
@@ -440,7 +440,11 @@ mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL)
|
||||
mark_as_advanced(WITH_CUDA_DYNLOAD)
|
||||
|
||||
# AMD HIP
|
||||
option(WITH_CYCLES_DEVICE_HIP "Enable Cycles AMD HIP support" OFF)
|
||||
if(WIN32)
|
||||
option(WITH_CYCLES_DEVICE_HIP "Enable Cycles AMD HIP support" ON)
|
||||
else()
|
||||
option(WITH_CYCLES_DEVICE_HIP "Enable Cycles AMD HIP support" OFF)
|
||||
endif()
|
||||
option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF)
|
||||
set(CYCLES_HIP_BINARIES_ARCH gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for")
|
||||
mark_as_advanced(WITH_CYCLES_DEVICE_HIP)
|
||||
|
@@ -81,4 +81,5 @@ if(NOT APPLE)
|
||||
set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE)
|
||||
set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE)
|
||||
set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_CYCLES_HIP_BINARIES ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
@@ -325,6 +325,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
default=1024,
|
||||
)
|
||||
|
||||
sample_offset: IntProperty(
|
||||
name="Sample Offset",
|
||||
description="Number of samples to skip when starting render",
|
||||
min=0, max=(1 << 24),
|
||||
default=0,
|
||||
)
|
||||
|
||||
time_limit: FloatProperty(
|
||||
name="Time Limit",
|
||||
description="Limit the render time (excluding synchronization time)."
|
||||
|
@@ -290,6 +290,9 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
|
||||
col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling)
|
||||
col.prop(cscene, "sampling_pattern", text="Pattern")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(cscene, "sample_offset")
|
||||
|
||||
layout.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
@@ -1051,7 +1054,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
|
||||
|
||||
|
||||
def has_geometry_visibility(ob):
|
||||
return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or
|
||||
return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT', 'VOLUME', 'POINTCLOUD', 'HAIR'}) or
|
||||
(ob.instance_type == 'COLLECTION' and ob.instance_collection))
|
||||
|
||||
|
||||
|
@@ -294,7 +294,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
|
||||
|
||||
object->set_visibility(visibility);
|
||||
|
||||
object->set_is_shadow_catcher(b_ob.is_shadow_catcher());
|
||||
object->set_is_shadow_catcher(b_ob.is_shadow_catcher() || b_parent.is_shadow_catcher());
|
||||
|
||||
float shadow_terminator_shading_offset = get_float(cobject, "shadow_terminator_offset");
|
||||
object->set_shadow_terminator_shading_offset(shadow_terminator_shading_offset);
|
||||
|
@@ -606,6 +606,19 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
|
||||
pass->set_type(bake_type_to_pass(bake_type, bake_filter));
|
||||
pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR));
|
||||
|
||||
if (pass->get_type() == PASS_COMBINED) {
|
||||
/* Filtering settings for combined pass. */
|
||||
Integrator *integrator = scene->integrator;
|
||||
integrator->set_use_direct_light((bake_filter & BL::BakeSettings::pass_filter_DIRECT) != 0);
|
||||
integrator->set_use_indirect_light((bake_filter & BL::BakeSettings::pass_filter_INDIRECT) !=
|
||||
0);
|
||||
integrator->set_use_diffuse((bake_filter & BL::BakeSettings::pass_filter_DIFFUSE) != 0);
|
||||
integrator->set_use_glossy((bake_filter & BL::BakeSettings::pass_filter_GLOSSY) != 0);
|
||||
integrator->set_use_transmission((bake_filter & BL::BakeSettings::pass_filter_TRANSMISSION) !=
|
||||
0);
|
||||
integrator->set_use_emission((bake_filter & BL::BakeSettings::pass_filter_EMIT) != 0);
|
||||
}
|
||||
|
||||
session->set_display_driver(nullptr);
|
||||
session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
|
||||
|
||||
|
@@ -835,18 +835,25 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
|
||||
/* samples */
|
||||
int samples = get_int(cscene, "samples");
|
||||
int preview_samples = get_int(cscene, "preview_samples");
|
||||
int sample_offset = get_int(cscene, "sample_offset");
|
||||
|
||||
if (background) {
|
||||
params.samples = samples;
|
||||
params.sample_offset = sample_offset;
|
||||
}
|
||||
else {
|
||||
params.samples = preview_samples;
|
||||
if (params.samples == 0)
|
||||
if (params.samples == 0) {
|
||||
params.samples = INT_MAX;
|
||||
}
|
||||
params.sample_offset = 0;
|
||||
}
|
||||
|
||||
/* Clamp sample offset. */
|
||||
params.sample_offset = clamp(params.sample_offset, 0, Integrator::MAX_SAMPLES);
|
||||
|
||||
/* Clamp samples. */
|
||||
params.samples = min(params.samples, Integrator::MAX_SAMPLES);
|
||||
params.samples = clamp(params.samples, 0, Integrator::MAX_SAMPLES - params.sample_offset);
|
||||
|
||||
/* Viewport Performance */
|
||||
params.pixel_size = b_engine.get_preview_pixel_size(b_scene);
|
||||
|
@@ -42,7 +42,7 @@ class CPUKernels {
|
||||
|
||||
IntegratorInitFunction integrator_init_from_camera;
|
||||
IntegratorInitFunction integrator_init_from_bake;
|
||||
IntegratorFunction integrator_intersect_closest;
|
||||
IntegratorShadeFunction integrator_intersect_closest;
|
||||
IntegratorFunction integrator_intersect_shadow;
|
||||
IntegratorFunction integrator_intersect_subsurface;
|
||||
IntegratorFunction integrator_intersect_volume_stack;
|
||||
|
@@ -931,7 +931,6 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
||||
{
|
||||
CUDAContextScope scope(this);
|
||||
|
||||
/* General variables for both architectures */
|
||||
string bind_name = mem.name;
|
||||
size_t dsize = datatype_size(mem.data_type);
|
||||
size_t size = mem.memory_size();
|
||||
@@ -1094,7 +1093,6 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
||||
|
||||
if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
|
||||
/* Kepler+, bindless textures. */
|
||||
CUDA_RESOURCE_DESC resDesc;
|
||||
memset(&resDesc, 0, sizeof(resDesc));
|
||||
|
||||
|
@@ -222,7 +222,6 @@ string HIPDevice::compile_kernel_get_common_cflags(const uint kernel_features)
|
||||
const string include_path = source_path;
|
||||
string cflags = string_printf(
|
||||
"-m%d "
|
||||
"--ptxas-options=\"-v\" "
|
||||
"--use_fast_math "
|
||||
"-DHIPCC "
|
||||
"-I\"%s\"",
|
||||
@@ -234,10 +233,7 @@ string HIPDevice::compile_kernel_get_common_cflags(const uint kernel_features)
|
||||
return cflags;
|
||||
}
|
||||
|
||||
string HIPDevice::compile_kernel(const uint kernel_features,
|
||||
const char *name,
|
||||
const char *base,
|
||||
bool force_ptx)
|
||||
string HIPDevice::compile_kernel(const uint kernel_features, const char *name, const char *base)
|
||||
{
|
||||
/* Compute kernel name. */
|
||||
int major, minor;
|
||||
@@ -247,7 +243,7 @@ string HIPDevice::compile_kernel(const uint kernel_features,
|
||||
hipGetDeviceProperties(&props, hipDevId);
|
||||
|
||||
/* gcnArchName can contain tokens after the arch name with features, ie.
|
||||
"gfx1010:sramecc-:xnack-" so we tokenize it to get the first part. */
|
||||
* `gfx1010:sramecc-:xnack-` so we tokenize it to get the first part. */
|
||||
char *arch = strtok(props.gcnArchName, ":");
|
||||
if (arch == NULL) {
|
||||
arch = props.gcnArchName;
|
||||
@@ -255,13 +251,11 @@ string HIPDevice::compile_kernel(const uint kernel_features,
|
||||
|
||||
/* Attempt to use kernel provided with Blender. */
|
||||
if (!use_adaptive_compilation()) {
|
||||
if (!force_ptx) {
|
||||
const string fatbin = path_get(string_printf("lib/%s_%s.fatbin", name, arch));
|
||||
VLOG(1) << "Testing for pre-compiled kernel " << fatbin << ".";
|
||||
if (path_exists(fatbin)) {
|
||||
VLOG(1) << "Using precompiled kernel.";
|
||||
return fatbin;
|
||||
}
|
||||
const string fatbin = path_get(string_printf("lib/%s_%s.fatbin", name, arch));
|
||||
VLOG(1) << "Testing for pre-compiled kernel " << fatbin << ".";
|
||||
if (path_exists(fatbin)) {
|
||||
VLOG(1) << "Using precompiled kernel.";
|
||||
return fatbin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,9 +292,9 @@ string HIPDevice::compile_kernel(const uint kernel_features,
|
||||
|
||||
# ifdef _WIN32
|
||||
if (!use_adaptive_compilation() && have_precompiled_kernels()) {
|
||||
if (major < 3) {
|
||||
if (!hipSupportsDevice(hipDevId)) {
|
||||
set_error(
|
||||
string_printf("HIP backend requires compute capability 3.0 or up, but found %d.%d. "
|
||||
string_printf("HIP backend requires compute capability 10.1 or up, but found %d.%d. "
|
||||
"Your GPU is not supported.",
|
||||
major,
|
||||
minor));
|
||||
@@ -380,10 +374,9 @@ string HIPDevice::compile_kernel(const uint kernel_features,
|
||||
|
||||
bool HIPDevice::load_kernels(const uint kernel_features)
|
||||
{
|
||||
/* TODO(sergey): Support kernels re-load for CUDA devices adaptive compile.
|
||||
/* TODO(sergey): Support kernels re-load for HIP devices adaptive compile.
|
||||
*
|
||||
* Currently re-loading kernel will invalidate memory pointers,
|
||||
* causing problems in cuCtxSynchronize.
|
||||
* Currently re-loading kernels will invalidate memory pointers.
|
||||
*/
|
||||
if (hipModule) {
|
||||
if (use_adaptive_compilation()) {
|
||||
@@ -904,7 +897,6 @@ void HIPDevice::tex_alloc(device_texture &mem)
|
||||
{
|
||||
HIPContextScope scope(this);
|
||||
|
||||
/* General variables for both architectures */
|
||||
string bind_name = mem.name;
|
||||
size_t dsize = datatype_size(mem.data_type);
|
||||
size_t size = mem.memory_size();
|
||||
@@ -1069,7 +1061,6 @@ void HIPDevice::tex_alloc(device_texture &mem)
|
||||
|
||||
if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT &&
|
||||
mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
|
||||
/* Kepler+, bindless textures. */
|
||||
hipResourceDesc resDesc;
|
||||
memset(&resDesc, 0, sizeof(resDesc));
|
||||
|
||||
@@ -1160,6 +1151,8 @@ bool HIPDevice::should_use_graphics_interop()
|
||||
* possible, but from the empiric measurements it can be considerably slower than using naive
|
||||
* pixels copy. */
|
||||
|
||||
/* Disable graphics interop for now, because of driver bug in 21.40. See T92972 */
|
||||
# if 0
|
||||
HIPContextScope scope(this);
|
||||
|
||||
int num_all_devices = 0;
|
||||
@@ -1178,6 +1171,7 @@ bool HIPDevice::should_use_graphics_interop()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@@ -93,10 +93,7 @@ class HIPDevice : public Device {
|
||||
|
||||
virtual string compile_kernel_get_common_cflags(const uint kernel_features);
|
||||
|
||||
string compile_kernel(const uint kernel_features,
|
||||
const char *name,
|
||||
const char *base = "hip",
|
||||
bool force_ptx = false);
|
||||
string compile_kernel(const uint kernel_features, const char *name, const char *base = "hip");
|
||||
|
||||
virtual bool load_kernels(const uint kernel_features) override;
|
||||
void reserve_local_memory(const uint kernel_features);
|
||||
|
@@ -48,7 +48,7 @@ class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop {
|
||||
HIPDeviceQueue *queue_ = nullptr;
|
||||
HIPDevice *device_ = nullptr;
|
||||
|
||||
/* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */
|
||||
/* OpenGL PBO which is currently registered as the destination for the HIP buffer. */
|
||||
uint opengl_pbo_id_ = 0;
|
||||
/* Buffer area in pixels of the corresponding PBO. */
|
||||
int64_t buffer_area_ = 0;
|
||||
|
@@ -886,8 +886,7 @@ bool OptiXDevice::denoise_configure_if_needed(DenoiseContext &context)
|
||||
denoiser_.scratch_offset = sizes.stateSizeInBytes;
|
||||
|
||||
/* Allocate denoiser state if tile size has changed since last setup. */
|
||||
denoiser_.state.alloc_to_device(denoiser_.scratch_offset + denoiser_.scratch_size +
|
||||
sizeof(float));
|
||||
denoiser_.state.alloc_to_device(denoiser_.scratch_offset + denoiser_.scratch_size);
|
||||
|
||||
/* Initialize denoiser state for the current tile size. */
|
||||
const OptixResult result = optixDenoiserSetup(
|
||||
@@ -971,16 +970,6 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass)
|
||||
|
||||
/* Finally run denoising. */
|
||||
OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */
|
||||
params.hdrIntensity = denoiser_.state.device_pointer + denoiser_.scratch_offset +
|
||||
denoiser_.scratch_size;
|
||||
|
||||
optix_assert(
|
||||
optixDenoiserComputeIntensity(denoiser_.optix_denoiser,
|
||||
denoiser_.queue.stream(),
|
||||
&color_layer,
|
||||
params.hdrIntensity,
|
||||
denoiser_.state.device_pointer + denoiser_.scratch_offset,
|
||||
denoiser_.scratch_size));
|
||||
|
||||
OptixDenoiserLayer image_layers = {};
|
||||
image_layers.input = color_layer;
|
||||
|
@@ -73,7 +73,8 @@ bool OptiXDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *a
|
||||
sizeof(device_ptr),
|
||||
cuda_stream_));
|
||||
|
||||
if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) {
|
||||
if (kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST ||
|
||||
kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) {
|
||||
cuda_device_assert(
|
||||
cuda_device_,
|
||||
cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParamsOptiX, render_buffer),
|
||||
|
@@ -33,7 +33,10 @@ unique_ptr<Denoiser> Denoiser::create(Device *path_trace_device, const DenoisePa
|
||||
return make_unique<OptiXDenoiser>(path_trace_device, params);
|
||||
}
|
||||
|
||||
return make_unique<OIDNDenoiser>(path_trace_device, params);
|
||||
/* Always fallback to OIDN. */
|
||||
DenoiseParams oidn_params = params;
|
||||
oidn_params.type = DENOISER_OPENIMAGEDENOISE;
|
||||
return make_unique<OIDNDenoiser>(path_trace_device, oidn_params);
|
||||
}
|
||||
|
||||
Denoiser::Denoiser(Device *path_trace_device, const DenoiseParams ¶ms)
|
||||
|
@@ -380,7 +380,10 @@ void PathTrace::path_trace(RenderWork &render_work)
|
||||
PathTraceWork *path_trace_work = path_trace_works_[i].get();
|
||||
|
||||
PathTraceWork::RenderStatistics statistics;
|
||||
path_trace_work->render_samples(statistics, render_work.path_trace.start_sample, num_samples);
|
||||
path_trace_work->render_samples(statistics,
|
||||
render_work.path_trace.start_sample,
|
||||
num_samples,
|
||||
render_work.path_trace.sample_offset);
|
||||
|
||||
const double work_time = time_dt() - work_start_time;
|
||||
work_balance_infos_[i].time_spent += work_time;
|
||||
@@ -849,7 +852,8 @@ void PathTrace::progress_update_if_needed(const RenderWork &render_work)
|
||||
const int2 tile_size = get_render_tile_size();
|
||||
const int num_samples_added = tile_size.x * tile_size.y * render_work.path_trace.num_samples;
|
||||
const int current_sample = render_work.path_trace.start_sample +
|
||||
render_work.path_trace.num_samples;
|
||||
render_work.path_trace.num_samples -
|
||||
render_work.path_trace.sample_offset;
|
||||
progress_->add_samples(num_samples_added, current_sample);
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,10 @@ class PathTraceWork {
|
||||
|
||||
/* Render given number of samples as a synchronous blocking call.
|
||||
* The samples are added to the render buffer associated with this work. */
|
||||
virtual void render_samples(RenderStatistics &statistics, int start_sample, int samples_num) = 0;
|
||||
virtual void render_samples(RenderStatistics &statistics,
|
||||
int start_sample,
|
||||
int samples_num,
|
||||
int sample_offset) = 0;
|
||||
|
||||
/* Copy render result from this work to the corresponding place of the GPU display.
|
||||
*
|
||||
|
@@ -71,14 +71,17 @@ void PathTraceWorkCPU::init_execution()
|
||||
|
||||
void PathTraceWorkCPU::render_samples(RenderStatistics &statistics,
|
||||
int start_sample,
|
||||
int samples_num)
|
||||
int samples_num,
|
||||
int sample_offset)
|
||||
{
|
||||
const int64_t image_width = effective_buffer_params_.width;
|
||||
const int64_t image_height = effective_buffer_params_.height;
|
||||
const int64_t total_pixels_num = image_width * image_height;
|
||||
|
||||
for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) {
|
||||
kernel_globals.start_profiling();
|
||||
if (device_->profiler.active()) {
|
||||
for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) {
|
||||
kernel_globals.start_profiling();
|
||||
}
|
||||
}
|
||||
|
||||
tbb::task_arena local_arena = local_tbb_arena_create(device_);
|
||||
@@ -97,6 +100,7 @@ void PathTraceWorkCPU::render_samples(RenderStatistics &statistics,
|
||||
work_tile.w = 1;
|
||||
work_tile.h = 1;
|
||||
work_tile.start_sample = start_sample;
|
||||
work_tile.sample_offset = sample_offset;
|
||||
work_tile.num_samples = 1;
|
||||
work_tile.offset = effective_buffer_params_.offset;
|
||||
work_tile.stride = effective_buffer_params_.stride;
|
||||
@@ -106,9 +110,10 @@ void PathTraceWorkCPU::render_samples(RenderStatistics &statistics,
|
||||
render_samples_full_pipeline(kernel_globals, work_tile, samples_num);
|
||||
});
|
||||
});
|
||||
|
||||
for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) {
|
||||
kernel_globals.stop_profiling();
|
||||
if (device_->profiler.active()) {
|
||||
for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) {
|
||||
kernel_globals.stop_profiling();
|
||||
}
|
||||
}
|
||||
|
||||
statistics.occupancy = 1.0f;
|
||||
|
@@ -48,7 +48,8 @@ class PathTraceWorkCPU : public PathTraceWork {
|
||||
|
||||
virtual void render_samples(RenderStatistics &statistics,
|
||||
int start_sample,
|
||||
int samples_num) override;
|
||||
int samples_num,
|
||||
int sample_offset) override;
|
||||
|
||||
virtual void copy_to_display(PathTraceDisplay *display,
|
||||
PassMode pass_mode,
|
||||
|
@@ -250,7 +250,8 @@ void PathTraceWorkGPU::init_execution()
|
||||
|
||||
void PathTraceWorkGPU::render_samples(RenderStatistics &statistics,
|
||||
int start_sample,
|
||||
int samples_num)
|
||||
int samples_num,
|
||||
int sample_offset)
|
||||
{
|
||||
/* Limit number of states for the tile and rely on a greedy scheduling of tiles. This allows to
|
||||
* add more work (because tiles are smaller, so there is higher chance that more paths will
|
||||
@@ -261,6 +262,7 @@ void PathTraceWorkGPU::render_samples(RenderStatistics &statistics,
|
||||
work_tile_scheduler_.reset(effective_buffer_params_,
|
||||
start_sample,
|
||||
samples_num,
|
||||
sample_offset,
|
||||
device_scene_->data.integrator.scrambling_distance);
|
||||
|
||||
enqueue_reset();
|
||||
@@ -437,7 +439,15 @@ void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel, const int num
|
||||
DCHECK_LE(work_size, max_num_paths_);
|
||||
|
||||
switch (kernel) {
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST:
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: {
|
||||
/* Closest ray intersection kernels with integrator state and render buffer. */
|
||||
void *d_render_buffer = (void *)buffers_->buffer.device_pointer;
|
||||
void *args[] = {&d_path_index, &d_render_buffer, const_cast<int *>(&work_size)};
|
||||
|
||||
queue_->enqueue(kernel, work_size, args);
|
||||
break;
|
||||
}
|
||||
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW:
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE:
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: {
|
||||
|
@@ -46,7 +46,8 @@ class PathTraceWorkGPU : public PathTraceWork {
|
||||
|
||||
virtual void render_samples(RenderStatistics &statistics,
|
||||
int start_sample,
|
||||
int samples_num) override;
|
||||
int samples_num,
|
||||
int sample_offset) override;
|
||||
|
||||
virtual void copy_to_display(PathTraceDisplay *display,
|
||||
PassMode pass_mode,
|
||||
|
@@ -88,6 +88,16 @@ int RenderScheduler::get_num_samples() const
|
||||
return num_samples_;
|
||||
}
|
||||
|
||||
void RenderScheduler::set_sample_offset(int sample_offset)
|
||||
{
|
||||
sample_offset_ = sample_offset;
|
||||
}
|
||||
|
||||
int RenderScheduler::get_sample_offset() const
|
||||
{
|
||||
return sample_offset_;
|
||||
}
|
||||
|
||||
void RenderScheduler::set_time_limit(double time_limit)
|
||||
{
|
||||
time_limit_ = time_limit;
|
||||
@@ -110,13 +120,15 @@ int RenderScheduler::get_num_rendered_samples() const
|
||||
return state_.num_rendered_samples;
|
||||
}
|
||||
|
||||
void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples)
|
||||
void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples, int sample_offset)
|
||||
{
|
||||
buffer_params_ = buffer_params;
|
||||
|
||||
update_start_resolution_divider();
|
||||
|
||||
set_num_samples(num_samples);
|
||||
set_start_sample(sample_offset);
|
||||
set_sample_offset(sample_offset);
|
||||
|
||||
/* In background mode never do lower resolution render preview, as it is not really supported
|
||||
* by the software. */
|
||||
@@ -171,7 +183,7 @@ void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples)
|
||||
|
||||
void RenderScheduler::reset_for_next_tile()
|
||||
{
|
||||
reset(buffer_params_, num_samples_);
|
||||
reset(buffer_params_, num_samples_, sample_offset_);
|
||||
}
|
||||
|
||||
bool RenderScheduler::render_work_reschedule_on_converge(RenderWork &render_work)
|
||||
@@ -317,6 +329,7 @@ RenderWork RenderScheduler::get_render_work()
|
||||
|
||||
render_work.path_trace.start_sample = get_start_sample_to_path_trace();
|
||||
render_work.path_trace.num_samples = get_num_samples_to_path_trace();
|
||||
render_work.path_trace.sample_offset = get_sample_offset();
|
||||
|
||||
render_work.init_render_buffers = (render_work.path_trace.start_sample == get_start_sample());
|
||||
|
||||
|
@@ -39,6 +39,7 @@ class RenderWork {
|
||||
struct {
|
||||
int start_sample = 0;
|
||||
int num_samples = 0;
|
||||
int sample_offset = 0;
|
||||
} path_trace;
|
||||
|
||||
struct {
|
||||
@@ -125,6 +126,9 @@ class RenderScheduler {
|
||||
void set_num_samples(int num_samples);
|
||||
int get_num_samples() const;
|
||||
|
||||
void set_sample_offset(int sample_offset);
|
||||
int get_sample_offset() const;
|
||||
|
||||
/* Time limit for the path tracing tasks, in minutes.
|
||||
* Zero disables the limit. */
|
||||
void set_time_limit(double time_limit);
|
||||
@@ -150,7 +154,7 @@ class RenderScheduler {
|
||||
|
||||
/* Reset scheduler, indicating that rendering will happen from scratch.
|
||||
* Resets current rendered state, as well as scheduling information. */
|
||||
void reset(const BufferParams &buffer_params, int num_samples);
|
||||
void reset(const BufferParams &buffer_params, int num_samples, int sample_offset);
|
||||
|
||||
/* Reset scheduler upon switching to a next tile.
|
||||
* Will keep the same number of samples and full-frame render parameters, but will reset progress
|
||||
@@ -419,6 +423,8 @@ class RenderScheduler {
|
||||
int start_sample_ = 0;
|
||||
int num_samples_ = 0;
|
||||
|
||||
int sample_offset_ = 0;
|
||||
|
||||
/* Limit in seconds for how long path tracing is allowed to happen.
|
||||
* Zero means no limit is applied. */
|
||||
double time_limit_ = 0.0;
|
||||
|
@@ -36,6 +36,7 @@ void WorkTileScheduler::set_max_num_path_states(int max_num_path_states)
|
||||
void WorkTileScheduler::reset(const BufferParams &buffer_params,
|
||||
int sample_start,
|
||||
int samples_num,
|
||||
int sample_offset,
|
||||
float scrambling_distance)
|
||||
{
|
||||
/* Image buffer parameters. */
|
||||
@@ -51,6 +52,7 @@ void WorkTileScheduler::reset(const BufferParams &buffer_params,
|
||||
/* Samples parameters. */
|
||||
sample_start_ = sample_start;
|
||||
samples_num_ = samples_num;
|
||||
sample_offset_ = sample_offset;
|
||||
|
||||
/* Initialize new scheduling. */
|
||||
reset_scheduler_state();
|
||||
@@ -111,6 +113,7 @@ bool WorkTileScheduler::get_work(KernelWorkTile *work_tile_, const int max_work_
|
||||
work_tile.h = tile_size_.height;
|
||||
work_tile.start_sample = sample_start_ + start_sample;
|
||||
work_tile.num_samples = min(tile_size_.num_samples, samples_num_ - start_sample);
|
||||
work_tile.sample_offset = sample_offset_;
|
||||
work_tile.offset = offset_;
|
||||
work_tile.stride = stride_;
|
||||
|
||||
|
@@ -41,6 +41,7 @@ class WorkTileScheduler {
|
||||
void reset(const BufferParams &buffer_params,
|
||||
int sample_start,
|
||||
int samples_num,
|
||||
int sample_offset,
|
||||
float scrambling_distance);
|
||||
|
||||
/* Get work for a device.
|
||||
@@ -79,6 +80,7 @@ class WorkTileScheduler {
|
||||
* (splitting into a smaller work tiles). */
|
||||
int sample_start_ = 0;
|
||||
int samples_num_ = 0;
|
||||
int sample_offset_ = 0;
|
||||
|
||||
/* Tile size which be scheduled for rendering. */
|
||||
TileSize tile_size_;
|
||||
|
@@ -37,7 +37,7 @@
|
||||
|
||||
KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_camera);
|
||||
KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_bake);
|
||||
KERNEL_INTEGRATOR_FUNCTION(intersect_closest);
|
||||
KERNEL_INTEGRATOR_SHADE_FUNCTION(intersect_closest);
|
||||
KERNEL_INTEGRATOR_FUNCTION(intersect_shadow);
|
||||
KERNEL_INTEGRATOR_FUNCTION(intersect_subsurface);
|
||||
KERNEL_INTEGRATOR_FUNCTION(intersect_volume_stack);
|
||||
|
@@ -112,7 +112,7 @@ CCL_NAMESPACE_BEGIN
|
||||
|
||||
DEFINE_INTEGRATOR_INIT_KERNEL(init_from_camera)
|
||||
DEFINE_INTEGRATOR_INIT_KERNEL(init_from_bake)
|
||||
DEFINE_INTEGRATOR_KERNEL(intersect_closest)
|
||||
DEFINE_INTEGRATOR_SHADE_KERNEL(intersect_closest)
|
||||
DEFINE_INTEGRATOR_KERNEL(intersect_subsurface)
|
||||
DEFINE_INTEGRATOR_KERNEL(intersect_volume_stack)
|
||||
DEFINE_INTEGRATOR_SHADE_KERNEL(shade_background)
|
||||
|
@@ -131,13 +131,14 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_closest,
|
||||
ccl_global const int *path_index_array,
|
||||
ccl_global float *render_buffer,
|
||||
const int work_size)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
|
||||
if (global_index < work_size) {
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_closest(NULL, state));
|
||||
ccl_gpu_kernel_call(integrator_intersect_closest(NULL, state, render_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,6 +524,26 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
* Film.
|
||||
*/
|
||||
|
||||
ccl_device_inline void kernel_gpu_film_convert_half_write(ccl_global uchar4 *rgba,
|
||||
const int rgba_offset,
|
||||
const int rgba_stride,
|
||||
const int x,
|
||||
const int y,
|
||||
const half4 half_pixel)
|
||||
{
|
||||
/* Work around HIP issue with half float display, see T92972. */
|
||||
#ifdef __KERNEL_HIP__
|
||||
ccl_global half *out = ((ccl_global half *)rgba) + (rgba_offset + y * rgba_stride + x) * 4;
|
||||
out[0] = half_pixel.x;
|
||||
out[1] = half_pixel.y;
|
||||
out[2] = half_pixel.z;
|
||||
out[3] = half_pixel.w;
|
||||
#else
|
||||
ccl_global half4 *out = ((ccl_global half4 *)rgba) + rgba_offset + y * rgba_stride + x;
|
||||
*out = half_pixel;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define KERNEL_FILM_CONVERT_VARIANT(variant, input_channel_count) \
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) \
|
||||
ccl_gpu_kernel_signature(film_convert_##variant, \
|
||||
@@ -588,8 +609,9 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
\
|
||||
film_apply_pass_pixel_overlays_rgba(&kfilm_convert, buffer, pixel); \
|
||||
\
|
||||
ccl_global half4 *out = ((ccl_global half4 *)rgba) + rgba_offset + y * rgba_stride + x; \
|
||||
*out = float4_to_half4_display(make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); \
|
||||
const half4 half_pixel = float4_to_half4_display( \
|
||||
make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); \
|
||||
kernel_gpu_film_convert_half_write(rgba, rgba_offset, rgba_stride, x, y, half_pixel); \
|
||||
}
|
||||
|
||||
/* 1 channel inputs */
|
||||
|
@@ -73,7 +73,8 @@ using namespace metal;
|
||||
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
|
||||
#define ccl_gpu_kernel_threads(block_num_threads)
|
||||
|
||||
/* convert a comma-separated list into a semicolon-separated list (so that we can generate a struct based on kernel entrypoint parameters) */
|
||||
/* Convert a comma-separated list into a semicolon-separated list
|
||||
* (so that we can generate a struct based on kernel entry-point parameters). */
|
||||
#define FN0()
|
||||
#define FN1(p1) p1;
|
||||
#define FN2(p1, p2) p1; p2;
|
||||
@@ -94,7 +95,8 @@ using namespace metal;
|
||||
#define GET_LAST_ARG(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, ...) p16
|
||||
#define PARAMS_MAKER(...) GET_LAST_ARG(__VA_ARGS__, FN16, FN15, FN14, FN13, FN12, FN11, FN10, FN9, FN8, FN7, FN6, FN5, FN4, FN3, FN2, FN1, FN0)
|
||||
|
||||
/* generate a struct containing the entrypoint parameters and a "run" method which can access them implicitly via this-> */
|
||||
/* Generate a struct containing the entry-point parameters and a "run"
|
||||
* method which can access them implicitly via this-> */
|
||||
#define ccl_gpu_kernel_signature(name, ...) \
|
||||
struct kernel_gpu_##name \
|
||||
{ \
|
||||
|
@@ -76,4 +76,4 @@ class MetalKernelContext {
|
||||
}
|
||||
# include "kernel/device/gpu/image.h"
|
||||
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
@@ -17,7 +17,7 @@
|
||||
; /* end of MetalKernelContext class definition */
|
||||
|
||||
/* Silently redirect into the MetalKernelContext instance */
|
||||
/* NOTE: These macros will need maintaining as entrypoints change */
|
||||
/* NOTE: These macros will need maintaining as entry-points change. */
|
||||
|
||||
#undef kernel_integrator_state
|
||||
#define kernel_integrator_state context.launch_params_metal.__integrator_state
|
||||
|
@@ -57,7 +57,7 @@ extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest()
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_closest(nullptr, path_index);
|
||||
integrator_intersect_closest(nullptr, path_index, __params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow()
|
||||
|
@@ -33,62 +33,72 @@ CCL_NAMESPACE_BEGIN
|
||||
* them separately. */
|
||||
|
||||
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval,
|
||||
const bool is_diffuse,
|
||||
const ClosureType closure_type,
|
||||
float3 value)
|
||||
{
|
||||
eval->diffuse = zero_float3();
|
||||
eval->glossy = zero_float3();
|
||||
|
||||
if (is_diffuse) {
|
||||
if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) {
|
||||
eval->diffuse = value;
|
||||
}
|
||||
else {
|
||||
else if (CLOSURE_IS_BSDF_GLOSSY(closure_type)) {
|
||||
eval->glossy = value;
|
||||
}
|
||||
|
||||
eval->sum = value;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval,
|
||||
const bool is_diffuse,
|
||||
float3 value,
|
||||
float mis_weight)
|
||||
const ClosureType closure_type,
|
||||
float3 value)
|
||||
{
|
||||
value *= mis_weight;
|
||||
|
||||
if (is_diffuse) {
|
||||
if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) {
|
||||
eval->diffuse += value;
|
||||
}
|
||||
else {
|
||||
else if (CLOSURE_IS_BSDF_GLOSSY(closure_type)) {
|
||||
eval->glossy += value;
|
||||
}
|
||||
|
||||
eval->sum += value;
|
||||
}
|
||||
|
||||
ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval)
|
||||
{
|
||||
return is_zero(eval->diffuse) && is_zero(eval->glossy);
|
||||
return is_zero(eval->sum);
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, float value)
|
||||
{
|
||||
eval->diffuse *= value;
|
||||
eval->glossy *= value;
|
||||
eval->sum *= value;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_mul3(ccl_private BsdfEval *eval, float3 value)
|
||||
{
|
||||
eval->diffuse *= value;
|
||||
eval->glossy *= value;
|
||||
eval->sum *= value;
|
||||
}
|
||||
|
||||
ccl_device_inline float3 bsdf_eval_sum(ccl_private const BsdfEval *eval)
|
||||
{
|
||||
return eval->diffuse + eval->glossy;
|
||||
return eval->sum;
|
||||
}
|
||||
|
||||
ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(ccl_private const BsdfEval *eval)
|
||||
ccl_device_inline float3 bsdf_eval_pass_diffuse_weight(ccl_private const BsdfEval *eval)
|
||||
{
|
||||
/* Ratio of diffuse and glossy to recover proportions for writing to render pass.
|
||||
/* Ratio of diffuse weight to recover proportions for writing to render pass.
|
||||
* We assume reflection, transmission and volume scatter to be exclusive. */
|
||||
return safe_divide_float3_float3(eval->diffuse, eval->diffuse + eval->glossy);
|
||||
return safe_divide_float3_float3(eval->diffuse, eval->sum);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 bsdf_eval_pass_glossy_weight(ccl_private const BsdfEval *eval)
|
||||
{
|
||||
/* Ratio of glossy weight to recover proportions for writing to render pass.
|
||||
* We assume reflection, transmission and volume scatter to be exclusive. */
|
||||
return safe_divide_float3_float3(eval->glossy, eval->sum);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
@@ -141,7 +151,8 @@ ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer(
|
||||
ccl_device_inline int kernel_accum_sample(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
int sample)
|
||||
int sample,
|
||||
int sample_offset)
|
||||
{
|
||||
if (kernel_data.film.pass_sample_count == PASS_UNUSED) {
|
||||
return sample;
|
||||
@@ -149,7 +160,8 @@ ccl_device_inline int kernel_accum_sample(KernelGlobals kg,
|
||||
|
||||
ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1);
|
||||
return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1) +
|
||||
sample_offset;
|
||||
}
|
||||
|
||||
ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg,
|
||||
@@ -351,37 +363,47 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg
|
||||
/* Directly visible, write to emission or background pass. */
|
||||
pass_offset = pass;
|
||||
}
|
||||
else if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ?
|
||||
((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect) :
|
||||
((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const float3 diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
const float3 glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
|
||||
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
/* Glossy is a subset of the throughput, reconstruct it here using the
|
||||
* diffuse-glossy ratio. */
|
||||
const float3 ratio = INTEGRATOR_STATE(state, path, diffuse_glossy_ratio);
|
||||
const float3 glossy_contribution = (one_float3() - ratio) * contribution;
|
||||
kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution);
|
||||
}
|
||||
/* Glossy */
|
||||
const int glossy_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect);
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= INTEGRATOR_STATE(state, path, diffuse_glossy_ratio);
|
||||
/* Transmission */
|
||||
const int transmission_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
|
||||
if (transmission_pass_offset != PASS_UNUSED) {
|
||||
/* Transmission is what remains if not diffuse and glossy, not stored explicitly to save
|
||||
* GPU memory. */
|
||||
const float3 transmission_weight = one_float3() - diffuse_weight - glossy_weight;
|
||||
kernel_write_pass_float3(buffer + transmission_pass_offset,
|
||||
transmission_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= diffuse_weight;
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
|
||||
/* Single write call for GPU coherence. */
|
||||
@@ -426,45 +448,56 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg,
|
||||
#ifdef __PASSES__
|
||||
if (kernel_data.film.light_pass_flag & PASS_ANY) {
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag);
|
||||
int pass_offset = PASS_UNUSED;
|
||||
|
||||
if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ?
|
||||
((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect) :
|
||||
((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
int pass_offset = PASS_UNUSED;
|
||||
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
/* Glossy is a subset of the throughput, reconstruct it here using the
|
||||
* diffuse-glossy ratio. */
|
||||
const float3 ratio = INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio);
|
||||
const float3 glossy_contribution = (one_float3() - ratio) * contribution;
|
||||
kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution);
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const float3 diffuse_weight = INTEGRATOR_STATE(state, shadow_path, pass_diffuse_weight);
|
||||
const float3 glossy_weight = INTEGRATOR_STATE(state, shadow_path, pass_glossy_weight);
|
||||
|
||||
/* Glossy */
|
||||
const int glossy_pass_offset = ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect);
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_weight * contribution);
|
||||
}
|
||||
|
||||
/* Transmission */
|
||||
const int transmission_pass_offset = ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
|
||||
if (transmission_pass_offset != PASS_UNUSED) {
|
||||
/* Transmission is what remains if not diffuse and glossy, not stored explicitly to save
|
||||
* GPU memory. */
|
||||
const float3 transmission_weight = one_float3() - diffuse_weight - glossy_weight;
|
||||
kernel_write_pass_float3(buffer + transmission_pass_offset,
|
||||
transmission_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= diffuse_weight;
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
/* Single write call for GPU coherence. */
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio);
|
||||
kernel_write_pass_float3(buffer + pass_offset, contribution);
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
|
||||
/* Single write call for GPU coherence. */
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + pass_offset, contribution);
|
||||
}
|
||||
|
||||
/* Write shadow pass. */
|
||||
if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) &&
|
||||
|
@@ -160,40 +160,6 @@ ccl_device_forceinline void kernel_write_denoising_features_volume(KernelGlobals
|
||||
}
|
||||
#endif /* __DENOISING_FEATURES__ */
|
||||
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
|
||||
/* Write shadow catcher passes on a bounce from the shadow catcher object. */
|
||||
ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data(
|
||||
KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_private const ShaderData *sd,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
if (!kernel_data.integrator.has_shadow_catcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
if (!kernel_shadow_catcher_is_path_split_bounce(kg, state, sd->object_flag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
/* Count sample for the shadow catcher object. */
|
||||
kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f);
|
||||
|
||||
/* Since the split is done, the sample does not contribute to the matte, so accumulate it as
|
||||
* transparency to the matte. */
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3,
|
||||
average(throughput));
|
||||
}
|
||||
|
||||
#endif /* __SHADOW_CATCHER__ */
|
||||
|
||||
ccl_device_inline size_t kernel_write_id_pass(ccl_global float *ccl_restrict buffer,
|
||||
size_t depth,
|
||||
float id,
|
||||
|
@@ -65,7 +65,8 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
|
||||
}
|
||||
|
||||
/* Always count the sample, even if the camera sample will reject the ray. */
|
||||
const int sample = kernel_accum_sample(kg, state, render_buffer, scheduled_sample);
|
||||
const int sample = kernel_accum_sample(
|
||||
kg, state, render_buffer, scheduled_sample, tile->sample_offset);
|
||||
|
||||
/* Setup render buffers. */
|
||||
const int index = INTEGRATOR_STATE(state, path, render_pixel_index);
|
||||
|
@@ -89,7 +89,8 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg,
|
||||
* This logic allows to both count actual number of samples per pixel, and to add samples to this
|
||||
* pixel after it was converged and samples were added somewhere else (in which case the
|
||||
* `scheduled_sample` will be different from actual number of samples in this pixel). */
|
||||
const int sample = kernel_accum_sample(kg, state, render_buffer, scheduled_sample);
|
||||
const int sample = kernel_accum_sample(
|
||||
kg, state, render_buffer, scheduled_sample, tile->sample_offset);
|
||||
|
||||
/* Initialize random number seed for path. */
|
||||
const uint rng_hash = path_rng_hash_init(kg, sample, x, y);
|
||||
|
@@ -88,7 +88,10 @@ ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg,
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
/* Split path if a shadow catcher was hit. */
|
||||
ccl_device_forceinline void integrator_split_shadow_catcher(
|
||||
KernelGlobals kg, IntegratorState state, ccl_private const Intersection *ccl_restrict isect)
|
||||
KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_private const Intersection *ccl_restrict isect,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
/* Test if we hit a shadow catcher object, and potentially split the path to continue tracing two
|
||||
* paths from here. */
|
||||
@@ -97,6 +100,8 @@ ccl_device_forceinline void integrator_split_shadow_catcher(
|
||||
return;
|
||||
}
|
||||
|
||||
kernel_write_shadow_catcher_bounce_data(kg, state, render_buffer);
|
||||
|
||||
/* Mark state as having done a shadow catcher split so that it stops contributing to
|
||||
* the shadow catcher matte pass, but keeps contributing to the combined pass. */
|
||||
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_HIT;
|
||||
@@ -191,6 +196,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel(
|
||||
KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_private const Intersection *ccl_restrict isect,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const bool hit)
|
||||
{
|
||||
/* Continue with volume kernel if we are inside a volume, regardless if we hit anything. */
|
||||
@@ -233,7 +239,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel(
|
||||
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
/* Handle shadow catcher. */
|
||||
integrator_split_shadow_catcher(kg, state, isect);
|
||||
integrator_split_shadow_catcher(kg, state, isect, render_buffer);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
@@ -253,7 +259,10 @@ ccl_device_forceinline void integrator_intersect_next_kernel(
|
||||
* volume shading and termination testing have already been done. */
|
||||
template<uint32_t current_kernel>
|
||||
ccl_device_forceinline void integrator_intersect_next_kernel_after_volume(
|
||||
KernelGlobals kg, IntegratorState state, ccl_private const Intersection *ccl_restrict isect)
|
||||
KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_private const Intersection *ccl_restrict isect,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
if (isect->prim != PRIM_NONE) {
|
||||
/* Hit a surface, continue with light or surface kernel. */
|
||||
@@ -278,7 +287,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume(
|
||||
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
/* Handle shadow catcher. */
|
||||
integrator_split_shadow_catcher(kg, state, isect);
|
||||
integrator_split_shadow_catcher(kg, state, isect, render_buffer);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -290,7 +299,9 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume(
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device void integrator_intersect_closest(KernelGlobals kg, IntegratorState state)
|
||||
ccl_device void integrator_intersect_closest(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
PROFILING_INIT(kg, PROFILING_INTERSECT_CLOSEST);
|
||||
|
||||
@@ -341,7 +352,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, IntegratorState s
|
||||
|
||||
/* Setup up next kernel to be executed. */
|
||||
integrator_intersect_next_kernel<DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST>(
|
||||
kg, state, &isect, hit);
|
||||
kg, state, &isect, render_buffer, hit);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -76,7 +76,7 @@ ccl_device void integrator_megakernel(KernelGlobals kg,
|
||||
if (queued_kernel) {
|
||||
switch (queued_kernel) {
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST:
|
||||
integrator_intersect_closest(kg, state);
|
||||
integrator_intersect_closest(kg, state, render_buffer);
|
||||
break;
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND:
|
||||
integrator_shade_background(kg, state, render_buffer);
|
||||
|
@@ -185,7 +185,7 @@ ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state,
|
||||
|
||||
/* Render pass categories. */
|
||||
if (bounce == 1) {
|
||||
flag |= (label & LABEL_TRANSMIT) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS;
|
||||
flag |= PATH_RAY_SURFACE_PASS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -191,14 +191,18 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
|
||||
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
|
||||
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
|
||||
shadow_flag |= (is_transmission) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS;
|
||||
shadow_flag |= PATH_RAY_SURFACE_PASS;
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval);
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
const float3 diffuse_glossy_ratio = (bounce == 0) ?
|
||||
bsdf_eval_diffuse_glossy_ratio(&bsdf_eval) :
|
||||
INTEGRATOR_STATE(state, path, diffuse_glossy_ratio);
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio;
|
||||
const float3 pass_diffuse_weight = (bounce == 0) ?
|
||||
bsdf_eval_pass_diffuse_weight(&bsdf_eval) :
|
||||
INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
const float3 pass_glossy_weight = (bounce == 0) ?
|
||||
bsdf_eval_pass_glossy_weight(&bsdf_eval) :
|
||||
INTEGRATOR_STATE(state, path, pass_glossy_weight);
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight;
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight;
|
||||
}
|
||||
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE(
|
||||
@@ -283,7 +287,9 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (INTEGRATOR_STATE(state, path, bounce) == 0) {
|
||||
INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = bsdf_eval_diffuse_glossy_ratio(
|
||||
INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = bsdf_eval_pass_diffuse_weight(
|
||||
&bsdf_eval);
|
||||
INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = bsdf_eval_pass_glossy_weight(
|
||||
&bsdf_eval);
|
||||
}
|
||||
}
|
||||
@@ -445,7 +451,7 @@ ccl_device bool integrate_surface(KernelGlobals kg,
|
||||
}
|
||||
#endif
|
||||
|
||||
shader_prepare_surface_closures(kg, state, &sd);
|
||||
shader_prepare_surface_closures(kg, state, &sd, path_flag);
|
||||
|
||||
#ifdef __HOLDOUT__
|
||||
/* Evaluate holdout. */
|
||||
@@ -492,10 +498,6 @@ ccl_device bool integrate_surface(KernelGlobals kg,
|
||||
kernel_write_denoising_features_surface(kg, state, &sd, render_buffer);
|
||||
#endif
|
||||
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
kernel_write_shadow_catcher_bounce_data(kg, state, &sd, render_buffer);
|
||||
#endif
|
||||
|
||||
/* Direct light. */
|
||||
PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT);
|
||||
integrate_surface_direct_light(kg, state, &sd, &rng_state);
|
||||
|
@@ -794,10 +794,11 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
||||
const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval);
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
const float3 diffuse_glossy_ratio = (bounce == 0) ?
|
||||
one_float3() :
|
||||
INTEGRATOR_STATE(state, path, diffuse_glossy_ratio);
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio;
|
||||
const float3 pass_diffuse_weight = (bounce == 0) ?
|
||||
one_float3() :
|
||||
INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight;
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = zero_float3();
|
||||
}
|
||||
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE(
|
||||
@@ -876,7 +877,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
|
||||
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase;
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3();
|
||||
INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_float3();
|
||||
INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_float3();
|
||||
}
|
||||
|
||||
/* Update path state */
|
||||
@@ -1024,7 +1026,7 @@ ccl_device void integrator_shade_volume(KernelGlobals kg,
|
||||
else {
|
||||
/* Continue to background, light or surface. */
|
||||
integrator_intersect_next_kernel_after_volume<DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME>(
|
||||
kg, state, &isect);
|
||||
kg, state, &isect, render_buffer);
|
||||
return;
|
||||
}
|
||||
#endif /* __VOLUME__ */
|
||||
|
@@ -105,8 +105,45 @@ ccl_device_inline void shader_copy_volume_phases(ccl_private ShaderVolumePhases
|
||||
|
||||
ccl_device_inline void shader_prepare_surface_closures(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
ccl_private ShaderData *sd)
|
||||
ccl_private ShaderData *sd,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
/* Filter out closures. */
|
||||
if (kernel_data.integrator.filter_closures) {
|
||||
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) {
|
||||
sd->closure_emission_background = zero_float3();
|
||||
}
|
||||
|
||||
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) {
|
||||
sd->flag &= ~SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
if (path_flag & PATH_RAY_CAMERA) {
|
||||
for (int i = 0; i < sd->num_closure; i++) {
|
||||
ccl_private ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
|
||||
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIFFUSE) {
|
||||
sc->type = CLOSURE_NONE_ID;
|
||||
sc->sample_weight = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
|
||||
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_GLOSSY) {
|
||||
sc->type = CLOSURE_NONE_ID;
|
||||
sc->sample_weight = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) {
|
||||
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSMISSION) {
|
||||
sc->type = CLOSURE_NONE_ID;
|
||||
sc->sample_weight = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Defensive sampling.
|
||||
*
|
||||
* We can likely also do defensive sampling at deeper bounces, particularly
|
||||
@@ -209,8 +246,7 @@ ccl_device_inline float _shader_bsdf_multi_eval(KernelGlobals kg,
|
||||
float3 eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf);
|
||||
|
||||
if (bsdf_pdf != 0.0f) {
|
||||
const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type);
|
||||
bsdf_eval_accum(result_eval, is_diffuse, eval * sc->weight, 1.0f);
|
||||
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
|
||||
sum_pdf += bsdf_pdf * sc->sample_weight;
|
||||
}
|
||||
}
|
||||
@@ -235,7 +271,7 @@ ccl_device_inline
|
||||
ccl_private BsdfEval *bsdf_eval,
|
||||
const uint light_shader_flags)
|
||||
{
|
||||
bsdf_eval_init(bsdf_eval, false, zero_float3());
|
||||
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_float3());
|
||||
|
||||
return _shader_bsdf_multi_eval(
|
||||
kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
|
||||
@@ -328,8 +364,7 @@ ccl_device int shader_bsdf_sample_closure(KernelGlobals kg,
|
||||
label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
|
||||
|
||||
if (*pdf != 0.0f) {
|
||||
const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type);
|
||||
bsdf_eval_init(bsdf_eval, is_diffuse, eval * sc->weight);
|
||||
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
|
||||
|
||||
if (sd->num_closure > 1) {
|
||||
const bool is_transmission = shader_bsdf_is_transmission(sd, *omega_in);
|
||||
@@ -655,7 +690,7 @@ ccl_device_inline float _shader_volume_phase_multi_eval(
|
||||
float3 eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf);
|
||||
|
||||
if (phase_pdf != 0.0f) {
|
||||
bsdf_eval_accum(result_eval, false, eval, 1.0f);
|
||||
bsdf_eval_accum(result_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
|
||||
sum_pdf += phase_pdf * svc->sample_weight;
|
||||
}
|
||||
|
||||
@@ -671,7 +706,7 @@ ccl_device float shader_volume_phase_eval(KernelGlobals kg,
|
||||
const float3 omega_in,
|
||||
ccl_private BsdfEval *phase_eval)
|
||||
{
|
||||
bsdf_eval_init(phase_eval, false, zero_float3());
|
||||
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_float3());
|
||||
|
||||
return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f);
|
||||
}
|
||||
@@ -729,7 +764,7 @@ ccl_device int shader_volume_phase_sample(KernelGlobals kg,
|
||||
label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, domega_in, pdf);
|
||||
|
||||
if (*pdf != 0.0f) {
|
||||
bsdf_eval_init(phase_eval, false, eval);
|
||||
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
|
||||
}
|
||||
|
||||
return label;
|
||||
@@ -752,7 +787,7 @@ ccl_device int shader_phase_sample_closure(KernelGlobals kg,
|
||||
label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
|
||||
|
||||
if (*pdf != 0.0f)
|
||||
bsdf_eval_init(phase_eval, false, eval);
|
||||
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/film/write_passes.h"
|
||||
#include "kernel/integrator/path_state.h"
|
||||
#include "kernel/integrator/state_util.h"
|
||||
|
||||
@@ -47,7 +48,7 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,6 +89,28 @@ ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(const uint32_t
|
||||
return path_flag & PATH_RAY_SHADOW_CATCHER_PASS;
|
||||
}
|
||||
|
||||
/* Write shadow catcher passes on a bounce from the shadow catcher object. */
|
||||
ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data(
|
||||
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index);
|
||||
const uint64_t render_buffer_offset = (uint64_t)render_pixel_index *
|
||||
kernel_data.film.pass_stride;
|
||||
ccl_global float *buffer = render_buffer + render_buffer_offset;
|
||||
|
||||
/* Count sample for the shadow catcher object. */
|
||||
kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f);
|
||||
|
||||
/* Since the split is done, the sample does not contribute to the matte, so accumulate it as
|
||||
* transparency to the matte. */
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3,
|
||||
average(throughput));
|
||||
}
|
||||
|
||||
#endif /* __SHADOW_CATCHER__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -46,8 +46,9 @@ KERNEL_STRUCT_MEMBER(shadow_path,
|
||||
float3,
|
||||
unshadowed_throughput,
|
||||
KERNEL_FEATURE_SHADOW_PASS | KERNEL_FEATURE_AO_ADDITIVE)
|
||||
/* Ratio of throughput to distinguish diffuse and glossy render passes. */
|
||||
KERNEL_STRUCT_MEMBER(shadow_path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
/* Ratio of throughput to distinguish diffuse / glossy / transmission render passes. */
|
||||
KERNEL_STRUCT_MEMBER(shadow_path, float3, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
KERNEL_STRUCT_MEMBER(shadow_path, float3, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
/* Number of intersections found by ray-tracing. */
|
||||
KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_END(shadow_path)
|
||||
|
@@ -60,8 +60,9 @@ KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_MEMBER(path, float, continuation_probability, KERNEL_FEATURE_PATH_TRACING)
|
||||
/* Throughput. */
|
||||
KERNEL_STRUCT_MEMBER(path, float3, throughput, KERNEL_FEATURE_PATH_TRACING)
|
||||
/* Ratio of throughput to distinguish diffuse and glossy render passes. */
|
||||
KERNEL_STRUCT_MEMBER(path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
/* Ratio of throughput to distinguish diffuse / glossy / transmission render passes. */
|
||||
KERNEL_STRUCT_MEMBER(path, float3, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
KERNEL_STRUCT_MEMBER(path, float3, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
/* Denoising. */
|
||||
KERNEL_STRUCT_MEMBER(path, float3, denoising_feature_throughput, KERNEL_FEATURE_DENOISING)
|
||||
/* Shader sorting. */
|
||||
|
@@ -79,7 +79,8 @@ ccl_device int subsurface_bounce(KernelGlobals kg,
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (INTEGRATOR_STATE(state, path, bounce) == 0) {
|
||||
INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3();
|
||||
INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_float3();
|
||||
INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_float3();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -286,27 +286,26 @@ enum PathRayFlag {
|
||||
PATH_RAY_DENOISING_FEATURES = (1U << 23U),
|
||||
|
||||
/* Render pass categories. */
|
||||
PATH_RAY_REFLECT_PASS = (1U << 24U),
|
||||
PATH_RAY_TRANSMISSION_PASS = (1U << 25U),
|
||||
PATH_RAY_VOLUME_PASS = (1U << 26U),
|
||||
PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS),
|
||||
PATH_RAY_SURFACE_PASS = (1U << 24U),
|
||||
PATH_RAY_VOLUME_PASS = (1U << 25U),
|
||||
PATH_RAY_ANY_PASS = (PATH_RAY_SURFACE_PASS | PATH_RAY_VOLUME_PASS),
|
||||
|
||||
/* Shadow ray is for a light or surface, or AO. */
|
||||
PATH_RAY_SHADOW_FOR_LIGHT = (1U << 27U),
|
||||
PATH_RAY_SHADOW_FOR_AO = (1U << 28U),
|
||||
PATH_RAY_SHADOW_FOR_LIGHT = (1U << 26U),
|
||||
PATH_RAY_SHADOW_FOR_AO = (1U << 27U),
|
||||
|
||||
/* A shadow catcher object was hit and the path was split into two. */
|
||||
PATH_RAY_SHADOW_CATCHER_HIT = (1U << 29U),
|
||||
PATH_RAY_SHADOW_CATCHER_HIT = (1U << 28U),
|
||||
|
||||
/* A shadow catcher object was hit and this path traces only shadow catchers, writing them into
|
||||
* their dedicated pass for later division.
|
||||
*
|
||||
* NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling
|
||||
* which is separate from the light passes. */
|
||||
PATH_RAY_SHADOW_CATCHER_PASS = (1U << 30U),
|
||||
PATH_RAY_SHADOW_CATCHER_PASS = (1U << 29U),
|
||||
|
||||
/* Path is evaluating background for an approximate shadow catcher with non-transparent film. */
|
||||
PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1U << 31U),
|
||||
PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1U << 30U),
|
||||
};
|
||||
|
||||
/* Configure ray visibility bits for rays and objects respectively,
|
||||
@@ -428,8 +427,19 @@ typedef enum CryptomatteType {
|
||||
typedef struct BsdfEval {
|
||||
float3 diffuse;
|
||||
float3 glossy;
|
||||
float3 sum;
|
||||
} BsdfEval;
|
||||
|
||||
/* Closure Filter */
|
||||
|
||||
typedef enum FilterClosures {
|
||||
FILTER_CLOSURE_EMISSION = (1 << 0),
|
||||
FILTER_CLOSURE_DIFFUSE = (1 << 1),
|
||||
FILTER_CLOSURE_GLOSSY = (1 << 2),
|
||||
FILTER_CLOSURE_TRANSMISSION = (1 << 3),
|
||||
FILTER_CLOSURE_DIRECT_LIGHT = (1 << 4),
|
||||
} FilterClosures;
|
||||
|
||||
/* Shader Flag */
|
||||
|
||||
typedef enum ShaderFlag {
|
||||
@@ -1186,7 +1196,11 @@ typedef struct KernelIntegrator {
|
||||
int has_shadow_catcher;
|
||||
float scrambling_distance;
|
||||
|
||||
/* Closure filter. */
|
||||
int filter_closures;
|
||||
|
||||
/* padding */
|
||||
int pad1, pad2, pad3;
|
||||
} KernelIntegrator;
|
||||
static_assert_align(KernelIntegrator, 16);
|
||||
|
||||
@@ -1410,6 +1424,7 @@ typedef struct KernelWorkTile {
|
||||
|
||||
uint start_sample;
|
||||
uint num_samples;
|
||||
uint sample_offset;
|
||||
|
||||
int offset;
|
||||
uint stride;
|
||||
|
@@ -187,8 +187,6 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
|
||||
kfilm->pass_transmission_indirect = PASS_UNUSED;
|
||||
kfilm->pass_volume_direct = PASS_UNUSED;
|
||||
kfilm->pass_volume_indirect = PASS_UNUSED;
|
||||
kfilm->pass_volume_direct = PASS_UNUSED;
|
||||
kfilm->pass_volume_indirect = PASS_UNUSED;
|
||||
kfilm->pass_shadow = PASS_UNUSED;
|
||||
|
||||
/* Mark passes as unused so that the kernel knows the pass is inaccessible. */
|
||||
@@ -673,13 +671,12 @@ uint Film::get_kernel_features(const Scene *scene) const
|
||||
kernel_features |= KERNEL_FEATURE_DENOISING;
|
||||
}
|
||||
|
||||
if (pass_type != PASS_NONE && pass_type != PASS_COMBINED &&
|
||||
pass_type <= PASS_CATEGORY_LIGHT_END) {
|
||||
if (pass_type >= PASS_DIFFUSE && pass_type <= PASS_VOLUME_INDIRECT) {
|
||||
kernel_features |= KERNEL_FEATURE_LIGHT_PASSES;
|
||||
}
|
||||
|
||||
if (pass_type == PASS_SHADOW) {
|
||||
kernel_features |= KERNEL_FEATURE_SHADOW_PASS;
|
||||
}
|
||||
if (pass_type == PASS_SHADOW) {
|
||||
kernel_features |= KERNEL_FEATURE_SHADOW_PASS;
|
||||
}
|
||||
|
||||
if (pass_type == PASS_AO) {
|
||||
|
@@ -63,6 +63,14 @@ NODE_DEFINE(Integrator)
|
||||
SOCKET_BOOLEAN(caustics_reflective, "Reflective Caustics", true);
|
||||
SOCKET_BOOLEAN(caustics_refractive, "Refractive Caustics", true);
|
||||
SOCKET_FLOAT(filter_glossy, "Filter Glossy", 0.0f);
|
||||
|
||||
SOCKET_BOOLEAN(use_direct_light, "Use Direct Light", true);
|
||||
SOCKET_BOOLEAN(use_indirect_light, "Use Indirect Light", true);
|
||||
SOCKET_BOOLEAN(use_diffuse, "Use Diffuse", true);
|
||||
SOCKET_BOOLEAN(use_glossy, "Use Glossy", true);
|
||||
SOCKET_BOOLEAN(use_transmission, "Use Transmission", true);
|
||||
SOCKET_BOOLEAN(use_emission, "Use Emission", true);
|
||||
|
||||
SOCKET_INT(seed, "Seed", 0);
|
||||
SOCKET_FLOAT(sample_clamp_direct, "Sample Clamp Direct", 0.0f);
|
||||
SOCKET_FLOAT(sample_clamp_indirect, "Sample Clamp Indirect", 0.0f);
|
||||
@@ -184,6 +192,27 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
||||
kintegrator->caustics_refractive = caustics_refractive;
|
||||
kintegrator->filter_glossy = (filter_glossy == 0.0f) ? FLT_MAX : 1.0f / filter_glossy;
|
||||
|
||||
kintegrator->filter_closures = 0;
|
||||
if (!use_direct_light) {
|
||||
kintegrator->filter_closures |= FILTER_CLOSURE_DIRECT_LIGHT;
|
||||
}
|
||||
if (!use_indirect_light) {
|
||||
kintegrator->min_bounce = 1;
|
||||
kintegrator->max_bounce = 1;
|
||||
}
|
||||
if (!use_diffuse) {
|
||||
kintegrator->filter_closures |= FILTER_CLOSURE_DIFFUSE;
|
||||
}
|
||||
if (!use_glossy) {
|
||||
kintegrator->filter_closures |= FILTER_CLOSURE_GLOSSY;
|
||||
}
|
||||
if (!use_transmission) {
|
||||
kintegrator->filter_closures |= FILTER_CLOSURE_TRANSMISSION;
|
||||
}
|
||||
if (!use_emission) {
|
||||
kintegrator->filter_closures |= FILTER_CLOSURE_EMISSION;
|
||||
}
|
||||
|
||||
kintegrator->seed = seed;
|
||||
|
||||
kintegrator->sample_clamp_direct = (sample_clamp_direct == 0.0f) ? FLT_MAX :
|
||||
|
@@ -56,6 +56,13 @@ class Integrator : public Node {
|
||||
NODE_SOCKET_API(bool, caustics_refractive)
|
||||
NODE_SOCKET_API(float, filter_glossy)
|
||||
|
||||
NODE_SOCKET_API(bool, use_direct_light);
|
||||
NODE_SOCKET_API(bool, use_indirect_light);
|
||||
NODE_SOCKET_API(bool, use_diffuse);
|
||||
NODE_SOCKET_API(bool, use_glossy);
|
||||
NODE_SOCKET_API(bool, use_transmission);
|
||||
NODE_SOCKET_API(bool, use_emission);
|
||||
|
||||
NODE_SOCKET_API(int, seed)
|
||||
|
||||
NODE_SOCKET_API(float, sample_clamp_direct)
|
||||
|
@@ -274,19 +274,26 @@ void OSLShaderManager::shading_system_init()
|
||||
|
||||
"diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */
|
||||
|
||||
"__unused__", /* PATH_RAY_SINGLE_PASS_DONE */
|
||||
"__unused__", /* PATH_RAY_TRANSPARENT_BACKGROUND */
|
||||
"__unused__", /* PATH_RAY_TERMINATE_IMMEDIATE */
|
||||
"__unused__", /* PATH_RAY_TERMINATE_AFTER_TRANSPARENT */
|
||||
"__unused__", /* PATH_RAY_EMISSION */
|
||||
"__unused__", /* PATH_RAY_SUBSURFACE */
|
||||
"__unused__", /* PATH_RAY_DENOISING_FEATURES */
|
||||
"__unused__", /* PATH_RAY_REFLECT_PASS */
|
||||
"__unused__", /* PATH_RAY_TRANSMISSION_PASS */
|
||||
"__unused__", /* PATH_RAY_VOLUME_PASS */
|
||||
"__unused__", /* PATH_RAY_SHADOW_FOR_LIGHT */
|
||||
"__unused__", /* PATH_RAY_SHADOW_CATCHER_HIT */
|
||||
"__unused__", /* PATH_RAY_SHADOW_CATCHER_PASS */
|
||||
/* Remaining irrelevant bits up to 32. */
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
};
|
||||
|
||||
const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]);
|
||||
|
@@ -262,6 +262,7 @@ RenderWork Session::run_update_for_next_iteration()
|
||||
}
|
||||
|
||||
render_scheduler_.set_num_samples(params.samples);
|
||||
render_scheduler_.set_start_sample(params.sample_offset);
|
||||
render_scheduler_.set_time_limit(params.time_limit);
|
||||
|
||||
while (have_tiles) {
|
||||
@@ -397,7 +398,7 @@ void Session::do_delayed_reset()
|
||||
|
||||
/* Tile and work scheduling. */
|
||||
tile_manager_.reset_scheduling(buffer_params_, get_effective_tile_size());
|
||||
render_scheduler_.reset(buffer_params_, params.samples);
|
||||
render_scheduler_.reset(buffer_params_, params.samples, params.sample_offset);
|
||||
|
||||
/* Passes. */
|
||||
/* When multiple tiles are used SAMPLE_COUNT pass is used to keep track of possible partial
|
||||
|
@@ -54,6 +54,7 @@ class SessionParams {
|
||||
|
||||
bool experimental;
|
||||
int samples;
|
||||
int sample_offset;
|
||||
int pixel_size;
|
||||
int threads;
|
||||
|
||||
@@ -75,6 +76,7 @@ class SessionParams {
|
||||
|
||||
experimental = false;
|
||||
samples = 1024;
|
||||
sample_offset = 0;
|
||||
pixel_size = 1;
|
||||
threads = 0;
|
||||
time_limit = 0.0;
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "util/path.h"
|
||||
#include "util/string.h"
|
||||
#include "util/system.h"
|
||||
#include "util/time.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
@@ -503,9 +504,9 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers)
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride);
|
||||
const double time_start = time_dt();
|
||||
|
||||
vector<float> pixel_storage;
|
||||
DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride);
|
||||
|
||||
const BufferParams &tile_params = tile_buffers.params;
|
||||
|
||||
@@ -515,13 +516,32 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers)
|
||||
const int64_t pass_stride = tile_params.pass_stride;
|
||||
const int64_t tile_row_stride = tile_params.width * pass_stride;
|
||||
|
||||
const int64_t xstride = pass_stride * sizeof(float);
|
||||
const int64_t ystride = xstride * tile_params.width;
|
||||
const int64_t zstride = ystride * tile_params.height;
|
||||
|
||||
vector<float> pixel_storage;
|
||||
const float *pixels = tile_buffers.buffer.data() + tile_params.window_x * pass_stride +
|
||||
tile_params.window_y * tile_row_stride;
|
||||
|
||||
/* If there is an overscan used for the tile copy pixels into single continuous block of memory
|
||||
* without any "gaps".
|
||||
* This is a workaround for bug in OIIO (https://github.com/OpenImageIO/oiio/pull/3176).
|
||||
* Our task reference: T93008. */
|
||||
if (tile_params.window_x || tile_params.window_y ||
|
||||
tile_params.window_width != tile_params.width ||
|
||||
tile_params.window_height != tile_params.height) {
|
||||
pixel_storage.resize(pass_stride * tile_params.window_width * tile_params.window_height);
|
||||
float *pixels_continuous = pixel_storage.data();
|
||||
|
||||
const int64_t pixels_row_stride = pass_stride * tile_params.width;
|
||||
const int64_t pixels_continuous_row_stride = pass_stride * tile_params.window_width;
|
||||
|
||||
for (int i = 0; i < tile_params.window_height; ++i) {
|
||||
memcpy(pixels_continuous, pixels, sizeof(float) * pixels_continuous_row_stride);
|
||||
pixels += pixels_row_stride;
|
||||
pixels_continuous += pixels_continuous_row_stride;
|
||||
}
|
||||
|
||||
pixels = pixel_storage.data();
|
||||
}
|
||||
|
||||
VLOG(3) << "Write tile at " << tile_x << ", " << tile_y;
|
||||
|
||||
/* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The
|
||||
@@ -531,6 +551,11 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers)
|
||||
*
|
||||
* The only thing we have to ensure is that the tile_x and tile_y are a multiple of the
|
||||
* image tile size, which happens in compute_render_tile_size. */
|
||||
|
||||
const int64_t xstride = pass_stride * sizeof(float);
|
||||
const int64_t ystride = xstride * tile_params.window_width;
|
||||
const int64_t zstride = ystride * tile_params.window_height;
|
||||
|
||||
if (!write_state_.tile_out->write_tiles(tile_x,
|
||||
tile_x + tile_params.window_width,
|
||||
tile_y,
|
||||
@@ -548,6 +573,8 @@ bool TileManager::write_tile(const RenderBuffers &tile_buffers)
|
||||
|
||||
++write_state_.num_tiles_written;
|
||||
|
||||
VLOG(3) << "Tile written in " << time_dt() - time_start << " seconds.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -589,6 +616,9 @@ void TileManager::finish_write_tiles()
|
||||
full_buffer_written_cb(write_state_.filename);
|
||||
}
|
||||
|
||||
VLOG(3) << "Tile file size is "
|
||||
<< string_human_readable_number(path_file_size(write_state_.filename)) << " bytes.";
|
||||
|
||||
/* Advance the counter upon explicit finish of the file.
|
||||
* Makes it possible to re-use tile manager for another scene, and avoids unnecessary increments
|
||||
* of the tile-file-within-session index. */
|
||||
|
@@ -171,4 +171,9 @@ bool Profiler::get_object(int object, uint64_t &samples, uint64_t &hits)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Profiler::active() const
|
||||
{
|
||||
return (worker != nullptr);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -96,6 +96,8 @@ class Profiler {
|
||||
bool get_shader(int shader, uint64_t &samples, uint64_t &hits);
|
||||
bool get_object(int object, uint64_t &samples, uint64_t &hits);
|
||||
|
||||
bool active() const;
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
|
@@ -728,13 +728,6 @@ extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthand
|
||||
*/
|
||||
extern unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle);
|
||||
|
||||
/**
|
||||
* Returns whether a context is rendered upside down compared to OpenGL. This only needs to be
|
||||
* called if there's a non-OpenGL context, which is really the exception.
|
||||
* So generally, this does not need to be called.
|
||||
*/
|
||||
extern int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle);
|
||||
|
||||
/**
|
||||
* Get the OpenGL frame-buffer handle that serves as a default frame-buffer.
|
||||
*/
|
||||
|
@@ -654,8 +654,8 @@ enum {
|
||||
GHOST_kXrContextDebug = (1 << 0),
|
||||
GHOST_kXrContextDebugTime = (1 << 1),
|
||||
# ifdef WIN32
|
||||
/* Needed to avoid issues with the SteamVR OpenGL graphics binding (use DirectX fallback
|
||||
instead). */
|
||||
/* Needed to avoid issues with the SteamVR OpenGL graphics binding
|
||||
* (use DirectX fallback instead). */
|
||||
GHOST_kXrContextGpuNVIDIA = (1 << 2),
|
||||
# endif
|
||||
};
|
||||
|
@@ -1245,7 +1245,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType
|
||||
|
||||
/* Convert the image in a RGBA 32bit format */
|
||||
/* As Core Graphics does not support contexts with non premutliplied alpha,
|
||||
we need to get alpha key values in a separate batch */
|
||||
* we need to get alpha key values in a separate batch */
|
||||
|
||||
/* First get RGB values w/o Alpha to avoid pre-multiplication,
|
||||
* 32bit but last byte is unused */
|
||||
@@ -1479,8 +1479,8 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
|
||||
CocoaWindow *cocoawindow;
|
||||
|
||||
/* [event window] returns other windows if mouse-over, that's OSX input standard
|
||||
however, if mouse exits window(s), the windows become inactive, until you click.
|
||||
We then fall back to the active window from ghost */
|
||||
* however, if mouse exits window(s), the windows become inactive, until you click.
|
||||
* We then fall back to the active window from ghost. */
|
||||
window = (GHOST_WindowCocoa *)m_windowManager->getWindowAssociatedWithOSWindow(
|
||||
(void *)[event window]);
|
||||
if (!window) {
|
||||
|
@@ -216,8 +216,9 @@ GHOST_XrAction::GHOST_XrAction(XrInstance instance,
|
||||
|
||||
XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO};
|
||||
strcpy(action_info.actionName, info.name);
|
||||
strcpy(action_info.localizedActionName, info.name); /* Just use same name for localized. This can
|
||||
be changed in the future if necessary. */
|
||||
|
||||
/* Just use same name for localized. This can be changed in the future if necessary. */
|
||||
strcpy(action_info.localizedActionName, info.name);
|
||||
|
||||
switch (info.type) {
|
||||
case GHOST_kXrActionTypeBooleanInput:
|
||||
|
@@ -97,8 +97,8 @@ static void read_vertices(const tinygltf::Accessor &accessor,
|
||||
validate_accessor(accessor, buffer_view, buffer, stride, packed_size);
|
||||
|
||||
/* Resize the vertices vector, if necessary, to include room for the attribute data.
|
||||
If there are multiple attributes for a primitive, the first one will resize, and the
|
||||
subsequent will not need to. */
|
||||
* If there are multiple attributes for a primitive, the first one will resize, and the
|
||||
* subsequent will not need to. */
|
||||
primitive.vertices.resize(accessor.count);
|
||||
|
||||
/* Copy the attribute value over from the glTF buffer into the appropriate vertex field. */
|
||||
@@ -147,9 +147,9 @@ static void read_indices(const tinygltf::Accessor &accessor,
|
||||
const tinygltf::Buffer &buffer,
|
||||
GHOST_XrPrimitive &primitive)
|
||||
{
|
||||
if (buffer_view.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER &&
|
||||
buffer_view.target != 0) { /* Allow 0 (not specified) even though spec doesn't seem to allow
|
||||
this (BoomBox GLB fails). */
|
||||
|
||||
/* Allow 0 (not specified) even though spec doesn't seem to allow this (BoomBox GLB fails). */
|
||||
if (buffer_view.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER && buffer_view.target != 0) {
|
||||
throw GHOST_XrException(
|
||||
"glTF: Accessor for indices uses bufferview with invalid 'target' type.");
|
||||
}
|
||||
@@ -164,8 +164,8 @@ static void read_indices(const tinygltf::Accessor &accessor,
|
||||
|
||||
validate_accessor(accessor, buffer_view, buffer, component_size_bytes, component_size_bytes);
|
||||
|
||||
if ((accessor.count % 3) != 0) { /* Since only triangles are supported, enforce that the number
|
||||
of indices is divisible by 3. */
|
||||
/* Since only triangles are supported, enforce that the number of indices is divisible by 3. */
|
||||
if ((accessor.count % 3) != 0) {
|
||||
throw GHOST_XrException("glTF: Unexpected number of indices for triangle primitive");
|
||||
}
|
||||
|
||||
|
@@ -1056,17 +1056,17 @@
|
||||
<ThemeInfo
|
||||
info_selected="#6080ff"
|
||||
info_selected_text="#000000"
|
||||
info_error="#FF0038ff"
|
||||
info_error="#ff0038ff"
|
||||
info_error_text="#000000"
|
||||
info_warning="#FFE900ff"
|
||||
info_warning="#ffe900ff"
|
||||
info_warning_text="#000000"
|
||||
info_info="#0068B3ff"
|
||||
info_info="#0068b3ff"
|
||||
info_info_text="#000000"
|
||||
info_debug="#B30095ff"
|
||||
info_debug="#b30095ff"
|
||||
info_debug_text="#000000"
|
||||
info_property="#44B300ff"
|
||||
info_property="#44b300ff"
|
||||
info_property_text="#000000"
|
||||
info_operator="#44B300ff"
|
||||
info_operator="#44b300ff"
|
||||
info_operator_text="#000000"
|
||||
>
|
||||
<space>
|
||||
@@ -1352,6 +1352,15 @@
|
||||
</panelcolors>
|
||||
</ThemeSpaceGeneric>
|
||||
</space>
|
||||
<space_list>
|
||||
<ThemeSpaceListGeneric
|
||||
list="#adadad"
|
||||
list_title="#c3c3c3"
|
||||
list_text="#c3c3c3"
|
||||
list_text_hi="#00ffff"
|
||||
>
|
||||
</ThemeSpaceListGeneric>
|
||||
</space_list>
|
||||
</ThemeSpreadsheet>
|
||||
</spreadsheet>
|
||||
<bone_color_sets>
|
||||
@@ -1530,6 +1539,44 @@
|
||||
>
|
||||
</ThemeCollectionColor>
|
||||
</collection_color>
|
||||
<strip_color>
|
||||
<ThemeStripColor
|
||||
color="#e2605b"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#f1a355"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#f1dc55"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#7bcc7b"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#5db6ea"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#8d59da"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#c673b8"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#7a5441"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
<ThemeStripColor
|
||||
color="#5f5f5f"
|
||||
>
|
||||
</ThemeStripColor>
|
||||
</strip_color>
|
||||
</Theme>
|
||||
<ThemeStyle>
|
||||
<panel_title>
|
||||
|
@@ -1097,6 +1097,7 @@ def km_outliner(params):
|
||||
# Fall through to generic context menu if the item(s) selected have no type specific actions.
|
||||
("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
|
||||
op_menu("OUTLINER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
|
||||
op_menu_pie("OUTLINER_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
|
||||
("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None),
|
||||
("outliner.show_hierarchy", {"type": 'HOME', "value": 'PRESS'}, None),
|
||||
@@ -1748,6 +1749,7 @@ def km_graph_editor(params):
|
||||
("graph.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
|
||||
("graph.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
|
||||
("graph.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
|
||||
op_menu_pie("GRAPH_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
("graph.fmodifier_add", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True},
|
||||
{"properties": [("only_active", False)]}),
|
||||
("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
|
||||
@@ -1815,6 +1817,7 @@ def km_image_generic(params):
|
||||
("image.cycle_render_slot", {"type": 'J', "value": 'PRESS', "repeat": True}, None),
|
||||
("image.cycle_render_slot", {"type": 'J', "value": 'PRESS', "alt": True, "repeat": True},
|
||||
{"properties": [("reverse", True)]}),
|
||||
op_menu_pie("IMAGE_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
])
|
||||
|
||||
if not params.legacy:
|
||||
@@ -2027,6 +2030,7 @@ def km_node_editor(params):
|
||||
("node.view_all", {"type": 'HOME', "value": 'PRESS'}, None),
|
||||
("node.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
|
||||
("node.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
|
||||
op_menu_pie("NODE_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
("node.delete", {"type": 'X', "value": 'PRESS'}, None),
|
||||
("node.delete", {"type": 'DEL', "value": 'PRESS'}, None),
|
||||
("node.delete_reconnect", {"type": 'X', "value": 'PRESS', "ctrl": True}, None),
|
||||
@@ -2389,6 +2393,7 @@ def km_dopesheet(params):
|
||||
("action.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
|
||||
("action.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
|
||||
("action.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
|
||||
op_menu_pie("DOPESHEET_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
|
||||
("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
|
||||
("transform.transform", {"type": 'G', "value": 'PRESS'},
|
||||
@@ -2503,6 +2508,7 @@ def km_nla_editor(params):
|
||||
("nla.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
|
||||
("nla.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
|
||||
("nla.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
|
||||
op_menu_pie("NLA_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
("nla.actionclip_add", {"type": 'A', "value": 'PRESS', "shift": True}, None),
|
||||
("nla.transition_add", {"type": 'T', "value": 'PRESS', "shift": True}, None),
|
||||
("nla.soundclip_add", {"type": 'K', "value": 'PRESS', "shift": True}, None),
|
||||
@@ -2835,6 +2841,7 @@ def km_sequencer(params):
|
||||
("sequencer.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None),
|
||||
op_menu("SEQUENCER_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}),
|
||||
op_menu("SEQUENCER_MT_change", {"type": 'C', "value": 'PRESS'}),
|
||||
op_menu_pie("SEQUENCER_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
("sequencer.slip", {"type": 'S', "value": 'PRESS'}, None),
|
||||
("wm.context_set_int", {"type": 'O', "value": 'PRESS'},
|
||||
{"properties": [("data_path", 'scene.sequence_editor.overlay_frame'), ("value", 0)]}),
|
||||
@@ -2892,6 +2899,7 @@ def km_sequencerpreview(params):
|
||||
{"properties": [("ratio", 0.25)]}),
|
||||
("sequencer.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'},
|
||||
{"properties": [("ratio", 0.125)]}),
|
||||
op_menu_pie("SEQUENCER_MT_preview_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
|
||||
# Edit.
|
||||
("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
|
||||
@@ -3042,6 +3050,7 @@ def km_clip(_params):
|
||||
op_menu_pie("CLIP_MT_solving_pie", {"type": 'S', "value": 'PRESS', "shift": True}),
|
||||
op_menu_pie("CLIP_MT_marker_pie", {"type": 'E', "value": 'PRESS', "shift": True}),
|
||||
op_menu_pie("CLIP_MT_reconstruction_pie", {"type": 'W', "value": 'PRESS', "shift": True}),
|
||||
op_menu_pie("CLIP_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@@ -1858,6 +1858,43 @@ class CLIP_MT_reconstruction_pie(Menu):
|
||||
pie.operator("clip.apply_solution_scale", icon='ARROW_LEFTRIGHT')
|
||||
|
||||
|
||||
class CLIP_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
|
||||
# View operators are not yet implemented in Dopesheet mode.
|
||||
return space.view != 'DOPESHEET'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
sc = context.space_data
|
||||
|
||||
pie = layout.menu_pie()
|
||||
|
||||
if sc.view == 'CLIP':
|
||||
pie.operator("clip.view_all")
|
||||
pie.operator("clip.view_selected", icon='ZOOM_SELECTED')
|
||||
|
||||
if sc.mode == 'MASK':
|
||||
pie.operator("clip.view_center_cursor")
|
||||
pie.separator()
|
||||
else:
|
||||
# Add spaces so items stay in the same position through all modes.
|
||||
pie.separator()
|
||||
pie.separator()
|
||||
|
||||
pie.operator("clip.view_all", text="Frame All Fit").fit_view = True
|
||||
|
||||
if sc.view == 'GRAPH':
|
||||
pie.operator_context = 'INVOKE_REGION_PREVIEW'
|
||||
pie.operator("clip.graph_view_all")
|
||||
pie.separator()
|
||||
pie.operator("clip.graph_center_current_frame")
|
||||
|
||||
|
||||
classes = (
|
||||
CLIP_UL_tracking_objects,
|
||||
CLIP_HT_header,
|
||||
@@ -1925,6 +1962,7 @@ classes = (
|
||||
CLIP_MT_tracking_pie,
|
||||
CLIP_MT_reconstruction_pie,
|
||||
CLIP_MT_solving_pie,
|
||||
CLIP_MT_view_pie,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
|
@@ -379,6 +379,18 @@ class DOPESHEET_MT_view(Menu):
|
||||
layout.menu("INFO_MT_area")
|
||||
|
||||
|
||||
class DOPESHEET_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("action.view_all")
|
||||
pie.operator("action.view_selected", icon='ZOOM_SELECTED')
|
||||
pie.operator("action.view_frame")
|
||||
|
||||
|
||||
class DOPESHEET_MT_select(Menu):
|
||||
bl_label = "Select"
|
||||
|
||||
@@ -778,6 +790,7 @@ classes = (
|
||||
DOPESHEET_MT_context_menu,
|
||||
DOPESHEET_MT_channel_context_menu,
|
||||
DOPESHEET_MT_snap_pie,
|
||||
DOPESHEET_MT_view_pie,
|
||||
DOPESHEET_PT_filters,
|
||||
DOPESHEET_PT_gpencil_mode,
|
||||
DOPESHEET_PT_gpencil_layer_masks,
|
||||
|
@@ -338,6 +338,18 @@ class GRAPH_MT_key_snap(Menu):
|
||||
layout.operator("graph.snap_cursor_value", text="Cursor Value to Selection")
|
||||
|
||||
|
||||
class GRAPH_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("graph.view_all")
|
||||
pie.operator("graph.view_selected", icon='ZOOM_SELECTED')
|
||||
pie.operator("graph.view_frame")
|
||||
|
||||
|
||||
class GRAPH_MT_delete(Menu):
|
||||
bl_label = "Delete"
|
||||
|
||||
@@ -468,6 +480,7 @@ classes = (
|
||||
GRAPH_MT_channel_context_menu,
|
||||
GRAPH_MT_pivot_pie,
|
||||
GRAPH_MT_snap_pie,
|
||||
GRAPH_MT_view_pie,
|
||||
GRAPH_PT_filters,
|
||||
)
|
||||
|
||||
|
@@ -591,6 +591,31 @@ class IMAGE_MT_uvs_snap_pie(Menu):
|
||||
).target = 'ADJACENT_UNSELECTED'
|
||||
|
||||
|
||||
class IMAGE_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
sima = context.space_data
|
||||
show_uvedit = sima.show_uvedit
|
||||
show_maskedit = sima.show_maskedit
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("image.view_all")
|
||||
|
||||
if show_uvedit or show_maskedit:
|
||||
pie.operator("image.view_selected", text="Frame Selected", icon='ZOOM_SELECTED')
|
||||
pie.operator("image.view_center_cursor", text="Center View to Cursor")
|
||||
else:
|
||||
# Add spaces so items stay in the same position through all modes.
|
||||
pie.separator()
|
||||
pie.separator()
|
||||
|
||||
pie.operator("image.view_zoom_ratio", text="Zoom 1:1").ratio = 1
|
||||
pie.operator("image.view_all", text="Frame All Fit").fit_view = True
|
||||
|
||||
|
||||
class IMAGE_HT_tool_header(Header):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_region_type = 'TOOL_HEADER'
|
||||
@@ -1633,6 +1658,7 @@ classes = (
|
||||
IMAGE_MT_mask_context_menu,
|
||||
IMAGE_MT_pivot_pie,
|
||||
IMAGE_MT_uvs_snap_pie,
|
||||
IMAGE_MT_view_pie,
|
||||
IMAGE_HT_tool_header,
|
||||
IMAGE_HT_header,
|
||||
IMAGE_MT_editor_menus,
|
||||
|
@@ -247,6 +247,18 @@ class NLA_MT_snap_pie(Menu):
|
||||
pie.operator("nla.snap", text="Selection to Nearest Marker").type = 'NEAREST_MARKER'
|
||||
|
||||
|
||||
class NLA_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("nla.view_all")
|
||||
pie.operator("nla.view_selected", icon='ZOOM_SELECTED')
|
||||
pie.operator("nla.view_frame")
|
||||
|
||||
|
||||
class NLA_MT_context_menu(Menu):
|
||||
bl_label = "NLA Context Menu"
|
||||
|
||||
@@ -300,6 +312,7 @@ classes = (
|
||||
NLA_MT_add,
|
||||
NLA_MT_edit_transform,
|
||||
NLA_MT_snap_pie,
|
||||
NLA_MT_view_pie,
|
||||
NLA_MT_context_menu,
|
||||
NLA_MT_channel_context_menu,
|
||||
NLA_PT_filters,
|
||||
|
@@ -367,6 +367,17 @@ class NODE_MT_node(Menu):
|
||||
layout.operator("node.read_viewlayers")
|
||||
|
||||
|
||||
class NODE_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("node.view_all")
|
||||
pie.operator("node.view_selected", icon='ZOOM_SELECTED')
|
||||
|
||||
|
||||
class NODE_PT_active_tool(ToolActivePanelHelper, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
@@ -877,6 +888,7 @@ classes = (
|
||||
NODE_MT_node,
|
||||
NODE_MT_node_color_context_menu,
|
||||
NODE_MT_context_menu,
|
||||
NODE_MT_view_pie,
|
||||
NODE_PT_material_slots,
|
||||
NODE_PT_node_color_presets,
|
||||
NODE_PT_active_node_generic,
|
||||
|
@@ -142,6 +142,17 @@ class OUTLINER_MT_context_menu_view(Menu):
|
||||
layout.operator("outliner.show_one_level", text="Hide One Level").open = False
|
||||
|
||||
|
||||
class OUTLINER_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("outliner.show_hierarchy")
|
||||
pie.operator("outliner.show_active", icon='ZOOM_SELECTED')
|
||||
|
||||
|
||||
class OUTLINER_MT_edit_datablocks(Menu):
|
||||
bl_label = "Edit"
|
||||
|
||||
@@ -471,6 +482,7 @@ classes = (
|
||||
OUTLINER_MT_asset,
|
||||
OUTLINER_MT_context_menu,
|
||||
OUTLINER_MT_context_menu_view,
|
||||
OUTLINER_MT_view_pie,
|
||||
OUTLINER_PT_filter,
|
||||
)
|
||||
|
||||
|
@@ -1173,6 +1173,31 @@ class SEQUENCER_MT_pivot_pie(Menu):
|
||||
pie.prop_enum(sequencer_tool_settings, "pivot_point", value='MEDIAN')
|
||||
|
||||
|
||||
class SEQUENCER_MT_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator("sequencer.view_all")
|
||||
pie.operator("sequencer.view_selected", text="Frame Selected", icon='ZOOM_SELECTED')
|
||||
|
||||
|
||||
class SEQUENCER_MT_preview_view_pie(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
pie.operator_context = 'INVOKE_REGION_PREVIEW'
|
||||
pie.operator("sequencer.view_all_preview")
|
||||
pie.operator("sequencer.view_selected", text="Frame Selected", icon='ZOOM_SELECTED')
|
||||
pie.separator()
|
||||
pie.operator("sequencer.view_zoom_ratio", text="Zoom 1:1").ratio = 1
|
||||
|
||||
|
||||
class SequencerButtonsPanel:
|
||||
bl_space_type = 'SEQUENCE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
@@ -2615,6 +2640,8 @@ classes = (
|
||||
SEQUENCER_MT_context_menu,
|
||||
SEQUENCER_MT_preview_context_menu,
|
||||
SEQUENCER_MT_pivot_pie,
|
||||
SEQUENCER_MT_view_pie,
|
||||
SEQUENCER_MT_preview_view_pie,
|
||||
|
||||
SEQUENCER_PT_color_tag_picker,
|
||||
|
||||
|
@@ -477,6 +477,16 @@ class USERPREF_PT_edit_weight_paint(EditingPanel, CenterAlignMixIn, Panel):
|
||||
col.active = view.use_weight_color_range
|
||||
col.template_color_ramp(view, "weight_color_range", expand=True)
|
||||
|
||||
class USERPREF_PT_edit_text_editor(EditingPanel, CenterAlignMixIn, Panel):
|
||||
bl_label = "Text Editor"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw_centered(self, context, layout):
|
||||
prefs = context.preferences
|
||||
edit = prefs.edit
|
||||
|
||||
layout.prop(edit, "use_text_edit_auto_close")
|
||||
|
||||
|
||||
class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel):
|
||||
bl_label = "Miscellaneous"
|
||||
@@ -2357,6 +2367,7 @@ classes = (
|
||||
USERPREF_PT_edit_annotations,
|
||||
USERPREF_PT_edit_weight_paint,
|
||||
USERPREF_PT_edit_gpencil,
|
||||
USERPREF_PT_edit_text_editor,
|
||||
USERPREF_PT_edit_misc,
|
||||
|
||||
USERPREF_PT_animation_timeline,
|
||||
|
@@ -754,6 +754,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeSwitch"),
|
||||
NodeItem("FunctionNodeRandomValue"),
|
||||
NodeItem("FunctionNodeAlignEulerToVector"),
|
||||
NodeItem("GeometryNodeSampleSound"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
|
||||
NodeItem("ShaderNodeVectorCurve"),
|
||||
|
@@ -65,7 +65,7 @@ void BLF_metrics_attach(int fontid, unsigned char *mem, int mem_size);
|
||||
|
||||
void BLF_aspect(int fontid, float x, float y, float z);
|
||||
void BLF_position(int fontid, float x, float y, float z);
|
||||
void BLF_size(int fontid, int size, int dpi);
|
||||
void BLF_size(int fontid, float size, int dpi);
|
||||
|
||||
/* goal: small but useful color API */
|
||||
void BLF_color4ubv(int fontid, const unsigned char rgba[4]);
|
||||
|
@@ -363,7 +363,7 @@ void BLF_position(int fontid, float x, float y, float z)
|
||||
}
|
||||
}
|
||||
|
||||
void BLF_size(int fontid, int size, int dpi)
|
||||
void BLF_size(int fontid, float size, int dpi)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
@@ -910,7 +910,7 @@ void BLF_state_print(int fontid)
|
||||
if (font) {
|
||||
printf("fontid %d %p\n", fontid, (void *)font);
|
||||
printf(" name: '%s'\n", font->name);
|
||||
printf(" size: %u\n", font->size);
|
||||
printf(" size: %f\n", font->size);
|
||||
printf(" dpi: %u\n", font->dpi);
|
||||
printf(" pos: %.6f %.6f %.6f\n", UNPACK3(font->pos));
|
||||
printf(" aspect: (%d) %.6f %.6f %.6f\n",
|
||||
|
@@ -1350,19 +1350,24 @@ void blf_font_free(FontBLF *font)
|
||||
/** \name Font Configure
|
||||
* \{ */
|
||||
|
||||
void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
|
||||
void blf_font_size(FontBLF *font, float size, unsigned int dpi)
|
||||
{
|
||||
blf_glyph_cache_acquire(font);
|
||||
|
||||
/* FreeType uses fixed-point integers in 64ths. */
|
||||
FT_F26Dot6 ft_size = lroundf(size * 64.0f);
|
||||
/* Adjust our size to be on even 64ths. */
|
||||
size = (float)ft_size / 64.0f;
|
||||
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi);
|
||||
if (gc && (font->size == size && font->dpi == dpi)) {
|
||||
/* Optimization: do not call FT_Set_Char_Size if size did not change. */
|
||||
}
|
||||
else {
|
||||
const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi);
|
||||
const FT_Error err = FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi);
|
||||
if (err) {
|
||||
/* FIXME: here we can go through the fixed size and choice a close one */
|
||||
printf("The current font don't support the size, %u and dpi, %u\n", size, dpi);
|
||||
printf("The current font don't support the size, %f and dpi, %u\n", size, dpi);
|
||||
}
|
||||
else {
|
||||
font->size = size;
|
||||
|
@@ -76,7 +76,7 @@ static FT_Fixed to_16dot16(double val)
|
||||
/**
|
||||
* Find a glyph cache that matches a size, DPI & styles.
|
||||
*/
|
||||
GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi)
|
||||
GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi)
|
||||
{
|
||||
GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first;
|
||||
while (gc) {
|
||||
|
@@ -52,7 +52,7 @@ struct FontBLF *blf_font_new(const char *name, const char *filename);
|
||||
struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size);
|
||||
void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size);
|
||||
|
||||
void blf_font_size(struct FontBLF *font, unsigned int size, unsigned int dpi);
|
||||
void blf_font_size(struct FontBLF *font, float size, unsigned int dpi);
|
||||
void blf_font_draw(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t str_len,
|
||||
@@ -130,9 +130,7 @@ int blf_font_count_missing_chars(struct FontBLF *font,
|
||||
|
||||
void blf_font_free(struct FontBLF *font);
|
||||
|
||||
struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font,
|
||||
unsigned int size,
|
||||
unsigned int dpi);
|
||||
struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, float size, unsigned int dpi);
|
||||
struct GlyphCacheBLF *blf_glyph_cache_new(struct FontBLF *font);
|
||||
struct GlyphCacheBLF *blf_glyph_cache_acquire(struct FontBLF *font);
|
||||
void blf_glyph_cache_release(struct FontBLF *font);
|
||||
|
@@ -65,7 +65,7 @@ typedef struct GlyphCacheBLF {
|
||||
struct GlyphCacheBLF *prev;
|
||||
|
||||
/* font size. */
|
||||
unsigned int size;
|
||||
float size;
|
||||
|
||||
/* and dpi. */
|
||||
unsigned int dpi;
|
||||
@@ -205,7 +205,7 @@ typedef struct FontBLF {
|
||||
unsigned int dpi;
|
||||
|
||||
/* font size. */
|
||||
unsigned int size;
|
||||
float size;
|
||||
|
||||
/* Column width when printing monospaced. */
|
||||
int fixed_width;
|
||||
|
@@ -95,7 +95,7 @@ void BLF_thumb_preview(const char *filename,
|
||||
const size_t draw_str_i18n_len = strlen(draw_str_i18n);
|
||||
int draw_str_i18n_nbr = 0;
|
||||
|
||||
blf_font_size(font, (unsigned int)MAX2(font_size_min, font_size_curr), dpi);
|
||||
blf_font_size(font, (float)MAX2(font_size_min, font_size_curr), dpi);
|
||||
gc = blf_glyph_cache_find(font, font->size, font->dpi);
|
||||
/* There will be no matching glyph cache if blf_font_size() failed to set font size. */
|
||||
if (!gc) {
|
||||
|
@@ -125,6 +125,17 @@ struct bActionGroup *BKE_action_group_find_name(struct bAction *act, const char
|
||||
/* Clear all 'temp' flags on all groups */
|
||||
void action_groups_clear_tempflags(struct bAction *act);
|
||||
|
||||
/**
|
||||
* Return whether the action has one unique point in time keyed.
|
||||
*
|
||||
* This is mostly for the pose library, which will have different behavior depending on whether an
|
||||
* Action corresponds to a "pose" (one keyframe) or "animation snippet" (multiple keyframes).
|
||||
*
|
||||
* \return `false` when there is no keyframe at all or keys on different points in time, `true`
|
||||
* when exactly one point in time is keyed.
|
||||
*/
|
||||
bool BKE_action_has_single_frame(const struct bAction *act);
|
||||
|
||||
/* Pose API ----------------- */
|
||||
|
||||
void BKE_pose_channel_free(struct bPoseChannel *pchan);
|
||||
|
@@ -31,16 +31,18 @@ struct ReportList;
|
||||
struct bContext;
|
||||
|
||||
/* copybuffer (wrapper for BKE_blendfile_write_partial) */
|
||||
void BKE_copybuffer_begin(struct Main *bmain_src);
|
||||
void BKE_copybuffer_tag_ID(struct ID *id);
|
||||
bool BKE_copybuffer_save(struct Main *bmain_src, const char *filename, struct ReportList *reports);
|
||||
void BKE_copybuffer_copy_begin(struct Main *bmain_src);
|
||||
void BKE_copybuffer_copy_tag_ID(struct ID *id);
|
||||
bool BKE_copybuffer_copy_end(struct Main *bmain_src,
|
||||
const char *filename,
|
||||
struct ReportList *reports);
|
||||
bool BKE_copybuffer_read(struct Main *bmain_dst,
|
||||
const char *libname,
|
||||
struct ReportList *reports,
|
||||
const uint64_t id_types_mask);
|
||||
int BKE_copybuffer_paste(struct bContext *C,
|
||||
const char *libname,
|
||||
const short flag,
|
||||
const int flag,
|
||||
struct ReportList *reports,
|
||||
const uint64_t id_types_mask);
|
||||
|
||||
|
@@ -45,7 +45,6 @@ typedef struct Global {
|
||||
|
||||
/** When set: `G_MAIN->name` contains valid relative base path. */
|
||||
bool relbase_valid;
|
||||
bool file_loaded;
|
||||
bool save_over;
|
||||
|
||||
/** Strings of recent opened files. */
|
||||
|
@@ -134,8 +134,10 @@ enum {
|
||||
LIB_ID_COPY_SHAPEKEY = 1 << 26,
|
||||
/** EXCEPTION! Specific deep-copy of node trees used e.g. for rendering purposes. */
|
||||
LIB_ID_COPY_NODETREE_LOCALIZE = 1 << 27,
|
||||
/** EXCEPTION! Specific handling of RB objects regarding collections differs depending whether we
|
||||
duplicate scene/collections, or objects. */
|
||||
/**
|
||||
* EXCEPTION! Specific handling of RB objects regarding collections differs depending whether we
|
||||
* duplicate scene/collections, or objects.
|
||||
*/
|
||||
LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28,
|
||||
|
||||
/* *** Helper 'defines' gathering most common flag sets. *** */
|
||||
|
@@ -1553,6 +1553,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_VOLUME_TO_MESH 1133
|
||||
#define GEO_NODE_INPUT_ID 1134
|
||||
#define GEO_NODE_SET_ID 1135
|
||||
#define GEO_NODE_SAMPLE_SOUND 1136
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -306,11 +306,23 @@ class BezierSpline final : public Spline {
|
||||
blender::Span<HandleType> handle_types_left() const;
|
||||
blender::MutableSpan<HandleType> handle_types_left();
|
||||
blender::Span<blender::float3> handle_positions_left() const;
|
||||
blender::MutableSpan<blender::float3> handle_positions_left();
|
||||
/**
|
||||
* Get writable access to the handle position.
|
||||
*
|
||||
* \param write_only: pass true for an uninitialized spline, this prevents accessing
|
||||
* uninitialized memory while auto-generating handles.
|
||||
*/
|
||||
blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false);
|
||||
blender::Span<HandleType> handle_types_right() const;
|
||||
blender::MutableSpan<HandleType> handle_types_right();
|
||||
blender::Span<blender::float3> handle_positions_right() const;
|
||||
blender::MutableSpan<blender::float3> handle_positions_right();
|
||||
/**
|
||||
* Get writable access to the handle position.
|
||||
*
|
||||
* \param write_only: pass true for an uninitialized spline, this prevents accessing
|
||||
* uninitialized memory while auto-generating handles.
|
||||
*/
|
||||
blender::MutableSpan<blender::float3> handle_positions_right(bool write_only = false);
|
||||
void ensure_auto_handles() const;
|
||||
|
||||
void translate(const blender::float3 &translation) override;
|
||||
|
@@ -51,6 +51,7 @@
|
||||
#include "BKE_anim_visualization.h"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_asset.h"
|
||||
#include "BKE_constraint.h"
|
||||
#include "BKE_deform.h"
|
||||
#include "BKE_fcurve.h"
|
||||
@@ -286,6 +287,30 @@ static void action_blend_read_expand(BlendExpander *expander, ID *id)
|
||||
}
|
||||
}
|
||||
|
||||
static IDProperty *action_asset_type_property(const bAction *action)
|
||||
{
|
||||
const bool is_single_frame = !BKE_action_has_single_frame(action);
|
||||
|
||||
IDPropertyTemplate idprop = {0};
|
||||
idprop.i = is_single_frame;
|
||||
|
||||
IDProperty *property = IDP_New(IDP_INT, &idprop, "is_single_frame");
|
||||
return property;
|
||||
}
|
||||
|
||||
static void action_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data)
|
||||
{
|
||||
bAction *action = (bAction *)asset_ptr;
|
||||
BLI_assert(GS(action->id.name) == ID_AC);
|
||||
|
||||
IDProperty *action_type = action_asset_type_property(action);
|
||||
BKE_asset_metadata_idprop_ensure(asset_data, action_type);
|
||||
}
|
||||
|
||||
AssetTypeInfo AssetType_AC = {
|
||||
/* pre_save_fn */ action_asset_pre_save,
|
||||
};
|
||||
|
||||
IDTypeInfo IDType_ID_AC = {
|
||||
.id_code = ID_AC,
|
||||
.id_filter = FILTER_ID_AC,
|
||||
@@ -312,6 +337,8 @@ IDTypeInfo IDType_ID_AC = {
|
||||
.blend_read_undo_preserve = NULL,
|
||||
|
||||
.lib_override_apply_post = NULL,
|
||||
|
||||
.asset_type_info = &AssetType_AC,
|
||||
};
|
||||
|
||||
/* ***************** Library data level operations on action ************** */
|
||||
@@ -1418,6 +1445,47 @@ bool action_has_motion(const bAction *act)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_action_has_single_frame(const struct bAction *act)
|
||||
{
|
||||
if (act == NULL || BLI_listbase_is_empty(&act->curves)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found_key = false;
|
||||
float found_key_frame = 0.0f;
|
||||
|
||||
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
|
||||
switch (fcu->totvert) {
|
||||
case 0:
|
||||
/* No keys, so impossible to come to a conclusion on this curve alone. */
|
||||
continue;
|
||||
case 1:
|
||||
/* Single key, which is the complex case, so handle below. */
|
||||
break;
|
||||
default:
|
||||
/* Multiple keys, so there is animation. */
|
||||
return false;
|
||||
}
|
||||
|
||||
const float this_key_frame = fcu->bezt != NULL ? fcu->bezt[0].vec[1][0] : fcu->fpt[0].vec[0];
|
||||
if (!found_key) {
|
||||
found_key = true;
|
||||
found_key_frame = this_key_frame;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The graph editor rounds to 1/1000th of a frame, so it's not necessary to be really precise
|
||||
* with these comparisons. */
|
||||
if (!compare_ff(found_key_frame, this_key_frame, 0.001f)) {
|
||||
/* This key differs from the already-found key, so this Action represents animation. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* There is only a single frame if we found at least one key. */
|
||||
return found_key;
|
||||
}
|
||||
|
||||
/* Calculate the extents of given action */
|
||||
void calc_action_range(const bAction *act, float *start, float *end, short incl_modifiers)
|
||||
{
|
||||
|
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::bke::tests {
|
||||
@@ -141,4 +143,97 @@ TEST(action_groups, ReconstructGroupsWithReordering)
|
||||
EXPECT_EQ(groupDcurve2.next, nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/* Allocate fcu->bezt, and also return a unique_ptr to it for easily freeing the memory. */
|
||||
std::unique_ptr<BezTriple[]> allocate_keyframes(FCurve *fcu, const size_t num_keyframes)
|
||||
{
|
||||
auto bezt_uptr = std::make_unique<BezTriple[]>(num_keyframes);
|
||||
fcu->bezt = bezt_uptr.get();
|
||||
return bezt_uptr;
|
||||
}
|
||||
|
||||
/* Append keyframe, assumes that fcu->bezt is allocated and has enough space. */
|
||||
void add_keyframe(FCurve *fcu, float x, float y)
|
||||
{
|
||||
/* The insert_keyframe functions are in the editors, so we cannot link to those here. */
|
||||
BezTriple the_keyframe;
|
||||
memset(&the_keyframe, 0, sizeof(the_keyframe));
|
||||
|
||||
/* Copied from insert_vert_fcurve() in keyframing.c. */
|
||||
the_keyframe.vec[0][0] = x - 1.0f;
|
||||
the_keyframe.vec[0][1] = y;
|
||||
the_keyframe.vec[1][0] = x;
|
||||
the_keyframe.vec[1][1] = y;
|
||||
the_keyframe.vec[2][0] = x + 1.0f;
|
||||
the_keyframe.vec[2][1] = y;
|
||||
|
||||
memcpy(&fcu->bezt[fcu->totvert], &the_keyframe, sizeof(the_keyframe));
|
||||
fcu->totvert++;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(action_assets, BKE_action_has_single_frame)
|
||||
{
|
||||
/* NULL action. */
|
||||
EXPECT_FALSE(BKE_action_has_single_frame(nullptr)) << "NULL Action cannot have a single frame.";
|
||||
|
||||
/* No FCurves. */
|
||||
{
|
||||
const bAction empty = {nullptr};
|
||||
EXPECT_FALSE(BKE_action_has_single_frame(&empty))
|
||||
<< "Action without FCurves cannot have a single frame.";
|
||||
}
|
||||
|
||||
/* One curve with one key. */
|
||||
{
|
||||
FCurve fcu = {nullptr};
|
||||
std::unique_ptr<BezTriple[]> bezt = allocate_keyframes(&fcu, 1);
|
||||
add_keyframe(&fcu, 1.0f, 2.0f);
|
||||
|
||||
bAction action = {nullptr};
|
||||
BLI_addtail(&action.curves, &fcu);
|
||||
|
||||
EXPECT_TRUE(BKE_action_has_single_frame(&action))
|
||||
<< "Action with one FCurve and one key should have single frame.";
|
||||
}
|
||||
|
||||
/* Two curves with one key each. */
|
||||
{
|
||||
FCurve fcu1 = {nullptr};
|
||||
FCurve fcu2 = {nullptr};
|
||||
std::unique_ptr<BezTriple[]> bezt1 = allocate_keyframes(&fcu1, 1);
|
||||
std::unique_ptr<BezTriple[]> bezt2 = allocate_keyframes(&fcu2, 1);
|
||||
add_keyframe(&fcu1, 1.0f, 327.0f);
|
||||
add_keyframe(&fcu2, 1.0f, 47.0f); /* Same X-coordinate as the other one. */
|
||||
|
||||
bAction action = {nullptr};
|
||||
BLI_addtail(&action.curves, &fcu1);
|
||||
BLI_addtail(&action.curves, &fcu2);
|
||||
|
||||
EXPECT_TRUE(BKE_action_has_single_frame(&action))
|
||||
<< "Two FCurves with keys on the same frame should have single frame.";
|
||||
|
||||
/* Modify the 2nd curve so it's keyed on a different frame. */
|
||||
fcu2.bezt[0].vec[1][0] = 2.0f;
|
||||
EXPECT_FALSE(BKE_action_has_single_frame(&action))
|
||||
<< "Two FCurves with keys on different frames should have animation.";
|
||||
}
|
||||
|
||||
/* One curve with two keys. */
|
||||
{
|
||||
FCurve fcu = {nullptr};
|
||||
std::unique_ptr<BezTriple[]> bezt = allocate_keyframes(&fcu, 2);
|
||||
add_keyframe(&fcu, 1.0f, 2.0f);
|
||||
add_keyframe(&fcu, 2.0f, 2.5f);
|
||||
|
||||
bAction action = {nullptr};
|
||||
BLI_addtail(&action.curves, &fcu);
|
||||
|
||||
EXPECT_FALSE(BKE_action_has_single_frame(&action))
|
||||
<< "Action with one FCurve and two keys must have animation.";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bke::tests
|
||||
|
@@ -57,20 +57,26 @@
|
||||
/** \name Copy/Paste `.blend`, partial saves.
|
||||
* \{ */
|
||||
|
||||
void BKE_copybuffer_begin(Main *bmain_src)
|
||||
/** Initialize a copy operation. */
|
||||
void BKE_copybuffer_copy_begin(Main *bmain_src)
|
||||
{
|
||||
BKE_blendfile_write_partial_begin(bmain_src);
|
||||
}
|
||||
|
||||
void BKE_copybuffer_tag_ID(ID *id)
|
||||
/** Mark an ID to be copied. Should only be called after a call to #BKE_copybuffer_copy_begin. */
|
||||
void BKE_copybuffer_copy_tag_ID(ID *id)
|
||||
{
|
||||
BKE_blendfile_write_partial_tag_ID(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return Success.
|
||||
* Finalize a copy operation into given .blend file 'buffer'.
|
||||
*
|
||||
* \param filename: Full path to the .blend file used as copy/paste buffer.
|
||||
*
|
||||
* \return true on success, false otherwise.
|
||||
*/
|
||||
bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *reports)
|
||||
bool BKE_copybuffer_copy_end(Main *bmain_src, const char *filename, ReportList *reports)
|
||||
{
|
||||
const int write_flags = 0;
|
||||
const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
|
||||
@@ -82,6 +88,16 @@ bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *repo
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste datablocks from the given .blend file 'buffer' (i.e. append them).
|
||||
*
|
||||
* Unlike #BKE_copybuffer_paste, it does not perform any instantiation of collections/objects/etc.
|
||||
*
|
||||
* \param libname: Full path to the .blend file used as copy/paste buffer.
|
||||
* \param id_types_mask: Only directly link IDs of those types from the given .blend file buffer.
|
||||
*
|
||||
* \return true on success, false otherwise.
|
||||
*/
|
||||
bool BKE_copybuffer_read(Main *bmain_dst,
|
||||
const char *libname,
|
||||
ReportList *reports,
|
||||
@@ -116,12 +132,22 @@ bool BKE_copybuffer_read(Main *bmain_dst,
|
||||
}
|
||||
|
||||
/**
|
||||
* \return Number of IDs directly pasted from the buffer
|
||||
* (does not includes indirectly pulled out ones).
|
||||
* Paste datablocks from the given .blend file 'buffer' (i.e. append them).
|
||||
*
|
||||
* Similar to #BKE_copybuffer_read, but also handles instantiation of collections/objects/etc.
|
||||
*
|
||||
* \param libname: Full path to the .blend file used as copy/paste buffer.
|
||||
* \param flag: A combination of #eBLOLibLinkFlags and ##eFileSel_Params_Flag to control
|
||||
* link/append behavior.
|
||||
* \note: Ignores #FILE_LINK flag, since it always appends IDs.
|
||||
* \param id_types_mask: Only directly link IDs of those types from the given .blend file buffer.
|
||||
*
|
||||
* \return Number of IDs directly pasted from the buffer (does not includes indirectly linked
|
||||
* ones).
|
||||
*/
|
||||
int BKE_copybuffer_paste(bContext *C,
|
||||
const char *libname,
|
||||
const short flag,
|
||||
const int flag,
|
||||
ReportList *reports,
|
||||
const uint64_t id_types_mask)
|
||||
{
|
||||
|
@@ -225,8 +225,8 @@ static SplinePtr spline_from_dna_bezier(const Nurb &nurb)
|
||||
Span<const BezTriple> src_points{nurb.bezt, nurb.pntsu};
|
||||
spline->resize(src_points.size());
|
||||
MutableSpan<float3> positions = spline->positions();
|
||||
MutableSpan<float3> handle_positions_left = spline->handle_positions_left();
|
||||
MutableSpan<float3> handle_positions_right = spline->handle_positions_right();
|
||||
MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true);
|
||||
MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true);
|
||||
MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left();
|
||||
MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right();
|
||||
MutableSpan<float> radii = spline->radii();
|
||||
|
@@ -422,7 +422,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Convert to worldspace. */
|
||||
/* Convert to world-space. */
|
||||
copy_v3_v3(tmp_loc, pchan->pose_head);
|
||||
mul_m4_v3(ob->obmat, tmp_loc);
|
||||
}
|
||||
|
@@ -369,7 +369,7 @@ static void checker_board_text(
|
||||
char text[3] = {'A', '1', '\0'};
|
||||
const int mono = blf_mono_font_render;
|
||||
|
||||
BLF_size(mono, 54, 72); /* hard coded size! */
|
||||
BLF_size(mono, 54.0f, 72); /* hard coded size! */
|
||||
|
||||
/* OCIO_TODO: using NULL as display will assume using sRGB display
|
||||
* this is correct since currently generated images are assumed to be in sRGB space,
|
||||
|
@@ -260,10 +260,16 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
||||
mul_m4_v3(mtx, mv->co);
|
||||
|
||||
if (do_vtargetmap) {
|
||||
/* compare location of the original and mirrored vertex, to see if they
|
||||
* should be mapped for merging */
|
||||
/* Compare location of the original and mirrored vertex,
|
||||
* to see if they should be mapped for merging.
|
||||
*
|
||||
* Always merge from the copied into the original vertices so it's possible to
|
||||
* generate a 1:1 mapping by scanning vertices from the beginning of the array
|
||||
* as is done in #BKE_editmesh_vert_coords_when_deformed. Without this,
|
||||
* the coordinates returned will sometimes point to the copied vertex locations, see: T91444.
|
||||
*/
|
||||
if (UNLIKELY(len_squared_v3v3(mv_prev->co, mv->co) < tolerance_sq)) {
|
||||
*vtmap_a = maxVerts + i;
|
||||
*vtmap_b = i;
|
||||
tot_vtargetmap++;
|
||||
|
||||
/* average location */
|
||||
@@ -271,10 +277,11 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
||||
copy_v3_v3(mv_prev->co, mv->co);
|
||||
}
|
||||
else {
|
||||
*vtmap_a = -1;
|
||||
*vtmap_b = -1;
|
||||
}
|
||||
|
||||
*vtmap_b = -1; /* fill here to avoid 2x loops */
|
||||
/* Fill here to avoid 2x loops. */
|
||||
*vtmap_a = -1;
|
||||
|
||||
vtmap_a++;
|
||||
vtmap_b++;
|
||||
|
@@ -113,7 +113,7 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag))
|
||||
|
||||
/**
|
||||
* \brief This function clears runtime cache of the given mesh.
|
||||
*
|
||||
*
|
||||
* Call this function to recalculate runtime data when used.
|
||||
*/
|
||||
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
|
||||
|
@@ -1542,7 +1542,7 @@ static bNodeSocket *make_socket(bNodeTree *ntree,
|
||||
}
|
||||
/* make the identifier unique */
|
||||
BLI_uniquename_cb(
|
||||
unique_identifier_check, lb, "socket", '.', auto_identifier, sizeof(auto_identifier));
|
||||
unique_identifier_check, lb, "socket", '_', auto_identifier, sizeof(auto_identifier));
|
||||
|
||||
bNodeSocket *sock = (bNodeSocket *)MEM_callocN(sizeof(bNodeSocket), "sock");
|
||||
sock->in_out = in_out;
|
||||
@@ -4778,6 +4778,9 @@ static OutputFieldDependency find_group_output_dependencies(
|
||||
/* Propagate search further to the left. */
|
||||
for (const InputSocketRef *origin_input_socket :
|
||||
gather_input_socket_dependencies(field_dependency, origin_node)) {
|
||||
if (!origin_input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (!field_state_by_socket_id[origin_input_socket->id()].is_single) {
|
||||
if (handled_sockets.add(origin_input_socket)) {
|
||||
sockets_to_check.push(origin_input_socket);
|
||||
@@ -4826,6 +4829,9 @@ static void propagate_data_requirements_from_right_to_left(
|
||||
const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies(
|
||||
field_dependency, *node);
|
||||
for (const InputSocketRef *input_socket : connected_inputs) {
|
||||
if (!input_socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (inferencing_interface.inputs[input_socket->index()] ==
|
||||
InputSocketFieldType::Implicit) {
|
||||
if (!input_socket->is_logically_linked()) {
|
||||
@@ -5923,6 +5929,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_raycast();
|
||||
register_node_type_geo_realize_instances();
|
||||
register_node_type_geo_rotate_instances();
|
||||
register_node_type_geo_sample_sound();
|
||||
register_node_type_geo_sample_texture();
|
||||
register_node_type_geo_scale_instances();
|
||||
register_node_type_geo_separate_components();
|
||||
|
@@ -1071,7 +1071,7 @@ static void link_recurs_seq(BlendDataReader *reader, ListBase *lb)
|
||||
/* Sanity check. */
|
||||
if (!SEQ_valid_strip_channel(seq)) {
|
||||
BLI_freelinkN(lb, seq);
|
||||
BLO_read_data_reports(reader)->count.vse_strips_skipped++;
|
||||
BLO_read_data_reports(reader)->count.sequence_strips_skipped++;
|
||||
}
|
||||
else if (seq->seqbase.first) {
|
||||
link_recurs_seq(reader, &seq->seqbase);
|
||||
|
@@ -86,6 +86,8 @@ static void sound_copy_data(Main *UNUSED(bmain),
|
||||
sound_dst->playback_handle = NULL;
|
||||
sound_dst->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock");
|
||||
BLI_spin_init(sound_dst->spinlock);
|
||||
sound_dst->tot_samples = 0;
|
||||
sound_dst->samples = NULL;
|
||||
|
||||
/* Just to be sure, should not have any value actually after reading time. */
|
||||
sound_dst->ipo = NULL;
|
||||
@@ -117,6 +119,9 @@ static void sound_free_data(ID *id)
|
||||
MEM_freeN(sound->spinlock);
|
||||
sound->spinlock = NULL;
|
||||
}
|
||||
if (sound->samples) {
|
||||
AUD_Sound_freeData(sound->samples);
|
||||
}
|
||||
}
|
||||
|
||||
static void sound_foreach_cache(ID *id,
|
||||
@@ -162,6 +167,8 @@ static void sound_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
sound->tags = 0;
|
||||
sound->handle = NULL;
|
||||
sound->playback_handle = NULL;
|
||||
sound->samples = NULL;
|
||||
sound->tot_samples = 0;
|
||||
|
||||
/* versioning stuff, if there was a cache, then we enable caching: */
|
||||
if (sound->cache) {
|
||||
|
@@ -142,11 +142,14 @@ Span<float3> BezierSpline::handle_positions_left() const
|
||||
this->ensure_auto_handles();
|
||||
return handle_positions_left_;
|
||||
}
|
||||
MutableSpan<float3> BezierSpline::handle_positions_left()
|
||||
MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only)
|
||||
{
|
||||
this->ensure_auto_handles();
|
||||
if (!write_only) {
|
||||
this->ensure_auto_handles();
|
||||
}
|
||||
return handle_positions_left_;
|
||||
}
|
||||
|
||||
Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
|
||||
{
|
||||
return handle_types_right_;
|
||||
@@ -160,9 +163,11 @@ Span<float3> BezierSpline::handle_positions_right() const
|
||||
this->ensure_auto_handles();
|
||||
return handle_positions_right_;
|
||||
}
|
||||
MutableSpan<float3> BezierSpline::handle_positions_right()
|
||||
MutableSpan<float3> BezierSpline::handle_positions_right(const bool write_only)
|
||||
{
|
||||
this->ensure_auto_handles();
|
||||
if (!write_only) {
|
||||
this->ensure_auto_handles();
|
||||
}
|
||||
return handle_positions_right_;
|
||||
}
|
||||
|
||||
|
@@ -1346,9 +1346,8 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a attribute domain to a up to 6 places (plus '\0' terminator) string using long number
|
||||
* names abbreviations. This function is designed to produce a compact representation of large
|
||||
* numbers.
|
||||
* Format a count to up to 6 places (plus '\0' terminator) string using long number
|
||||
* names abbreviations. Used to produce a compact representation of large numbers.
|
||||
*
|
||||
* 1 -> 1
|
||||
* 15 -> 15
|
||||
@@ -1362,8 +1361,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base
|
||||
* 1000000000 -> 1B
|
||||
* ...
|
||||
*
|
||||
* Dimension of 7 is the maximum length of the resulting string
|
||||
* A combination with 7 places would be -15.5K\0
|
||||
* Length of 7 is the maximum of the resulting string, for example, `-15.5K\0`.
|
||||
*/
|
||||
void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format)
|
||||
{
|
||||
|
@@ -124,7 +124,7 @@ typedef struct BlendFileReadReport {
|
||||
/* Number of proxies that failed to convert to library overrides. */
|
||||
int proxies_to_lib_overrides_failures;
|
||||
/* Number of sequencer strips that were not read because were in non-supported channels. */
|
||||
int vse_strips_skipped;
|
||||
int sequence_strips_skipped;
|
||||
} count;
|
||||
|
||||
/* Number of libraries which had overrides that needed to be resynced, and a single linked list
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user