Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "device/cpu/device.h"
#include "device/cpu/device_impl.h"
#include "device/device.h"
/* Used for `info.denoisers`. */
/* TODO(sergey): The denoisers are probably to be moved completely out of the device into their
* own class. But until then keep API consistent with how it used to work before. */
#include "util/guiding.h"
#include "util/openimagedenoise.h"
CCL_NAMESPACE_BEGIN
unique_ptr<Device> device_cpu_create(const DeviceInfo &info,
Stats &stats,
Profiler &profiler,
bool headless)
{
return make_unique<CPUDevice>(info, stats, profiler, headless);
}
void device_cpu_info(vector<DeviceInfo> &devices)
{
DeviceInfo info;
info.type = DEVICE_CPU;
info.description = system_cpu_brand_string();
info.id = "CPU";
info.num = 0;
info.has_osl = true;
info.has_nanovdb = true;
info.has_profiling = true;
if (guiding_supported()) {
info.has_guiding = true;
}
else {
info.has_guiding = false;
}
if (openimagedenoise_supported()) {
info.denoisers |= DENOISER_OPENIMAGEDENOISE;
}
devices.insert(devices.begin(), info);
}
string device_cpu_capabilities()
{
return system_cpu_support_avx2() ? "AVX2" : "";
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "util/string.h"
#include "util/unique_ptr.h"
#include "util/vector.h"
CCL_NAMESPACE_BEGIN
class Device;
class DeviceInfo;
class Profiler;
class Stats;
unique_ptr<Device> device_cpu_create(const DeviceInfo &info,
Stats &stats,
Profiler &profiler,
bool headless);
void device_cpu_info(vector<DeviceInfo> &devices);
string device_cpu_capabilities();
CCL_NAMESPACE_END

View File

@@ -0,0 +1,369 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "device/cpu/device_impl.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
/* So ImathMath is included before our kernel_cpu_compat. */
#ifdef WITH_OSL
/* So no context pollution happens from indirectly included windows.h */
# ifdef _WIN32
# include "util/windows.h"
# endif
# include <OSL/oslexec.h>
#endif
#ifdef WITH_EMBREE
# include <embree4/rtcore.h>
#endif
#include "device/cpu/kernel.h"
#include "device/device.h"
#include "kernel/device/cpu/kernel.h"
#include "kernel/globals.h"
#include "kernel/types.h"
#include "bvh/embree.h"
#include "session/buffers.h"
#include "util/guiding.h"
#include "util/log.h"
#include "util/progress.h"
#include "util/task.h"
#include "util/types_image.h"
CCL_NAMESPACE_BEGIN
CPUDevice::CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool headless_)
: Device(info_, stats_, profiler_, headless_)
{
image_info = make_unique<device_vector<KernelImageInfo>>(this, "image_info", MEM_GLOBAL);
/* Pick any kernel, all of them are supposed to have same level of microarchitecture
* optimization. */
LOG_INFO << "Using " << get_cpu_kernels().integrator_init_from_camera.get_uarch_name()
<< " CPU kernels.";
if (info.cpu_threads == 0) {
info.cpu_threads = TaskScheduler::max_concurrency();
}
#ifdef WITH_EMBREE
embree_device = rtcNewDevice("verbose=0");
#endif
}
CPUDevice::~CPUDevice()
{
#ifdef WITH_EMBREE
rtcReleaseDevice(embree_device);
#endif
image_info->free();
}
BVHLayoutMask CPUDevice::get_bvh_layout_mask(uint /*kernel_features*/) const
{
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_BVH2;
#ifdef WITH_EMBREE
bvh_layout_mask |= BVH_LAYOUT_EMBREE;
#endif /* WITH_EMBREE */
return bvh_layout_mask;
}
void CPUDevice::mem_alloc(device_memory &mem)
{
if (mem.type == MEM_IMAGE_TEXTURE) {
assert(!"mem_alloc not supported for images.");
}
else if (mem.type == MEM_GLOBAL) {
assert(!"mem_alloc not supported for global memory.");
}
else {
LOG_DEBUG << "Buffer allocate: " << mem.log_name() << ", "
<< string_human_readable_number(mem.memory_size()) << " bytes. ("
<< string_human_readable_size(mem.memory_size()) << ")";
if (mem.type == MEM_DEVICE_ONLY) {
size_t alignment = MIN_ALIGNMENT_DEVICE_MEMORY;
void *data = util_aligned_malloc(mem.memory_size(), alignment);
mem.device_pointer = (device_ptr)data;
}
else {
assert(!(mem.host_pointer == nullptr && mem.memory_size() > 0));
mem.device_pointer = (device_ptr)mem.host_pointer;
}
mem.device_size = mem.memory_size();
stats.mem_alloc(mem.device_size);
}
}
void CPUDevice::mem_copy_to(device_memory &mem)
{
if (mem.type == MEM_GLOBAL) {
global_free(mem);
global_alloc(mem);
}
else if (mem.type == MEM_IMAGE_TEXTURE) {
image_free((device_image &)mem);
image_alloc((device_image &)mem);
}
else {
if (!mem.device_pointer) {
mem_alloc(mem);
}
/* copy is no-op */
}
}
void CPUDevice::mem_move_to_host(device_memory & /*mem*/)
{
/* no-op */
}
void CPUDevice::mem_copy_from(
device_memory & /*mem*/, size_t /*y*/, size_t /*w*/, size_t /*h*/, size_t /*elem*/)
{
/* no-op */
}
void CPUDevice::mem_or_from_device(device_memory & /*mem*/)
{
/* Nothing to do data is already in host buffer. */
}
void CPUDevice::mem_zero(device_memory &mem)
{
if (!mem.device_pointer) {
mem_alloc(mem);
}
if (mem.device_pointer) {
memset((void *)mem.device_pointer, 0, mem.memory_size());
}
}
void CPUDevice::mem_free(device_memory &mem)
{
if (mem.type == MEM_GLOBAL) {
global_free(mem);
}
else if (mem.type == MEM_IMAGE_TEXTURE) {
image_free((device_image &)mem);
}
else if (mem.device_pointer) {
if (mem.type == MEM_DEVICE_ONLY) {
util_aligned_free((void *)mem.device_pointer, mem.memory_size());
}
mem.device_pointer = 0;
stats.mem_free(mem.device_size);
mem.device_size = 0;
}
}
device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, const size_t offset, size_t /*size*/)
{
return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset));
}
void CPUDevice::const_copy_to(const char *name, void *host, const size_t size)
{
#ifdef WITH_EMBREE
if (strcmp(name, "data") == 0) {
assert(size <= sizeof(KernelData));
/* Update scene handle (since it is different for each device on multi devices).
* This must be a raw pointer copy since at some points during scene update this
* pointer may be invalid. */
KernelData *const data = (KernelData *)host;
data->device_bvh = embree_traversable;
}
#endif
/* Update both the main one, and the per-thread globals in case of updates during
* render from e.g. the texture cache. */
kernel_const_copy(&kernel_globals, name, host, size);
for (ThreadKernelGlobalsCPU &kg : kernel_thread_globals_) {
kernel_const_copy(&kg, name, host, size);
}
}
void CPUDevice::global_alloc(device_memory &mem)
{
LOG_DEBUG << "Global memory allocate: " << mem.log_name() << ", "
<< string_human_readable_number(mem.memory_size()) << " bytes. ("
<< string_human_readable_size(mem.memory_size()) << ")";
/* Update both the main one, and the per-thread globals in case of updates during
* render from e.g. the texture cache. */
kernel_global_memory_copy(&kernel_globals, mem.global_name(), mem.host_pointer, mem.data_size);
for (ThreadKernelGlobalsCPU &kg : kernel_thread_globals_) {
kernel_global_memory_copy(&kg, mem.global_name(), mem.host_pointer, mem.data_size);
}
mem.device_pointer = (device_ptr)mem.host_pointer;
mem.device_size = mem.memory_size();
stats.mem_alloc(mem.device_size);
}
void CPUDevice::global_free(device_memory &mem)
{
if (mem.device_pointer) {
mem.device_pointer = 0;
stats.mem_free(mem.device_size);
mem.device_size = 0;
}
}
void CPUDevice::image_alloc(device_image &mem)
{
LOG_DEBUG << "Texture allocate: " << mem.log_name() << ", "
<< string_human_readable_number(mem.memory_size()) << " bytes. ("
<< string_human_readable_size(mem.memory_size()) << ")";
mem.device_pointer = (device_ptr)mem.host_pointer;
mem.device_size = mem.memory_size();
stats.mem_alloc(mem.device_size);
const uint image_info_id = mem.image_info_id;
if (image_info_id >= image_info->size()) {
/* Geometric growth to amortize reallocation cost. */
const size_t new_size = max(size_t(image_info_id) + 128, image_info->size() * 2);
unique_ptr<device_vector<KernelImageInfo>> new_info =
make_unique<device_vector<KernelImageInfo>>(this, "image_info", MEM_GLOBAL);
new_info->resize(new_size);
if (image_info->size() > 0) {
std::copy_n(image_info->data(), image_info->size(), new_info->data());
}
/* Move old vector to backup list to keep memory alive for concurrent access. */
old_image_infos.push_back(std::move(image_info));
image_info = std::move(new_info);
/* Update kernel globals pointers immediately. */
image_info->copy_to_device();
}
(*image_info)[image_info_id] = mem.info;
(*image_info)[image_info_id].data = (uint64_t)mem.host_pointer;
}
void CPUDevice::image_free(device_image &mem)
{
if (mem.device_pointer) {
mem.device_pointer = 0;
stats.mem_free(mem.device_size);
mem.device_size = 0;
}
}
bool CPUDevice::has_unified_memory() const
{
return true;
}
bool CPUDevice::has_unified_image_memory() const
{
return true;
}
void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
{
#ifdef WITH_EMBREE
if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE ||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE ||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_METAL_EMBREE ||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_HIPRT_EMBREE ||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_EMBREEGPU_EMBREE)
{
BVHEmbree *const bvh_embree = static_cast<BVHEmbree *>(bvh);
if (refit) {
bvh_embree->refit(progress);
}
else {
bvh_embree->build(progress, &stats, embree_device);
}
if (bvh->params.top_level) {
# if RTC_VERSION >= 40400
embree_traversable = rtcGetSceneTraversable(bvh_embree->scene);
# else
embree_traversable = bvh_embree->scene;
# endif
}
}
else
#endif
{
Device::build_bvh(bvh, progress, refit);
}
}
void *CPUDevice::get_guiding_device() const
{
#if defined(WITH_PATH_GUIDING)
if (!guiding_device) {
if (guiding_device_type() == 8) {
guiding_device = make_unique<openpgl::cpp::Device>(PGL_DEVICE_TYPE_CPU_8);
}
else if (guiding_device_type() == 4) {
guiding_device = make_unique<openpgl::cpp::Device>(PGL_DEVICE_TYPE_CPU_4);
}
}
return guiding_device.get();
#else
return nullptr;
#endif
}
vector<ThreadKernelGlobalsCPU> *CPUDevice::acquire_cpu_kernel_thread_globals()
{
assert(kernel_thread_globals_.empty());
kernel_thread_globals_.clear();
OSLGlobals *osl_globals = get_cpu_osl_memory();
for (int i = 0; i < info.cpu_threads; i++) {
kernel_thread_globals_.emplace_back(kernel_globals, osl_globals, profiler, i);
}
return &kernel_thread_globals_;
}
void CPUDevice::release_cpu_kernel_thread_globals()
{
kernel_thread_globals_.clear();
old_image_infos.clear();
}
OSLGlobals *CPUDevice::get_cpu_osl_memory()
{
#ifdef WITH_OSL
return &osl_globals;
#else
return nullptr;
#endif
}
void CPUDevice::set_image_cache_func(KernelImageLoadRequestedCPU image_load_requested_cpu,
KernelImageLoadRequestedGPU /*image_load_requested_gpu*/)
{
kernel_globals.image_load_requested_cpu = image_load_requested_cpu;
}
bool CPUDevice::load_kernels(const uint /*kernel_features*/)
{
return true;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,101 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
/* So ImathMath is included before our kernel_cpu_compat. */
#ifdef WITH_OSL
# include <cstdint> /* Needed before `sdlexec.h` for `int32_t` with GCC 15.1. */
/* So no context pollution happens from indirectly included windows.h */
# ifdef _WIN32
# include "util/windows.h"
# endif
# include <OSL/oslexec.h>
#endif
#ifdef WITH_EMBREE
# include <embree4/rtcore.h>
#endif
#include "device/cpu/kernel.h"
#include "device/device.h"
#include "device/memory.h"
// clang-format off
#include "kernel/device/cpu/kernel.h"
#include "kernel/globals.h"
#include "kernel/osl/globals.h"
// clang-format on
#include "util/guiding.h" // IWYU pragma: keep
#include "util/list.h"
#include "util/unique_ptr.h"
CCL_NAMESPACE_BEGIN
class CPUDevice : public Device {
public:
KernelGlobalsCPU kernel_globals;
vector<ThreadKernelGlobalsCPU> kernel_thread_globals_;
unique_ptr<device_vector<KernelImageInfo>> image_info;
list<unique_ptr<device_vector<KernelImageInfo>>> old_image_infos;
#ifdef WITH_OSL
OSLGlobals osl_globals;
#endif
#ifdef WITH_EMBREE
# if RTC_VERSION >= 40400
RTCTraversable embree_traversable = nullptr;
# else
RTCScene embree_traversable = nullptr;
# endif
RTCDevice embree_device;
#endif
#if defined(WITH_PATH_GUIDING)
mutable unique_ptr<openpgl::cpp::Device> guiding_device;
#endif
CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool headless_);
~CPUDevice() override;
BVHLayoutMask get_bvh_layout_mask(uint /*kernel_features*/) const override;
void mem_alloc(device_memory &mem) override;
void mem_copy_to(device_memory &mem) override;
void mem_move_to_host(device_memory &mem) override;
void mem_copy_from(
device_memory &mem, const size_t y, size_t w, const size_t h, size_t elem) override;
void mem_zero(device_memory &mem) override;
void mem_free(device_memory &mem) override;
void mem_or_from_device(device_memory &mem) override;
device_ptr mem_alloc_sub_ptr(device_memory &mem, const size_t offset, size_t /*size*/) override;
void const_copy_to(const char *name, void *host, const size_t size) override;
void global_alloc(device_memory &mem);
void global_free(device_memory &mem);
void image_alloc(device_image &mem);
void image_free(device_image &mem);
bool has_unified_memory() const override;
bool has_unified_image_memory() const override;
void build_bvh(BVH *bvh, Progress &progress, bool refit) override;
void *get_guiding_device() const override;
vector<ThreadKernelGlobalsCPU> *acquire_cpu_kernel_thread_globals() override;
void release_cpu_kernel_thread_globals() override;
OSLGlobals *get_cpu_osl_memory() override;
void set_image_cache_func(KernelImageLoadRequestedCPU image_load_requested_cpu,
KernelImageLoadRequestedGPU image_load_requested_gpu) override;
protected:
bool load_kernels(uint /*kernel_features*/) override;
};
CCL_NAMESPACE_END

View File

@@ -0,0 +1,59 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "device/cpu/kernel.h"
#include "kernel/device/cpu/kernel.h"
CCL_NAMESPACE_BEGIN
#define KERNEL_FUNCTIONS(name) KERNEL_NAME_EVAL(cpu, name), KERNEL_NAME_EVAL(cpu_avx2, name)
#define REGISTER_KERNEL(name) name(KERNEL_FUNCTIONS(name))
#define REGISTER_KERNEL_FILM_CONVERT(name) \
film_convert_##name(KERNEL_FUNCTIONS(film_convert_##name)), \
film_convert_half_rgba_##name(KERNEL_FUNCTIONS(film_convert_half_rgba_##name))
CPUKernels::CPUKernels()
: /* Integrator. */
REGISTER_KERNEL(integrator_init_from_camera),
REGISTER_KERNEL(integrator_init_from_bake),
REGISTER_KERNEL(integrator_megakernel),
/* Shader evaluation. */
REGISTER_KERNEL(shader_eval_displace),
REGISTER_KERNEL(shader_eval_background),
REGISTER_KERNEL(shader_eval_curve_shadow_transparency),
REGISTER_KERNEL(shader_eval_volume_density),
/* Adaptive sampling. */
REGISTER_KERNEL(adaptive_sampling_convergence_check),
REGISTER_KERNEL(adaptive_sampling_filter_x),
REGISTER_KERNEL(adaptive_sampling_filter_y),
/* Volume Scattering Probability Guiding. */
REGISTER_KERNEL(volume_guiding_filter_x),
REGISTER_KERNEL(volume_guiding_filter_y),
/* Cryptomatte. */
REGISTER_KERNEL(cryptomatte_postprocess),
/* Film Convert. */
REGISTER_KERNEL_FILM_CONVERT(depth),
REGISTER_KERNEL_FILM_CONVERT(mist),
REGISTER_KERNEL_FILM_CONVERT(volume_majorant),
REGISTER_KERNEL_FILM_CONVERT(sample_count),
REGISTER_KERNEL_FILM_CONVERT(float),
REGISTER_KERNEL_FILM_CONVERT(light_path),
REGISTER_KERNEL_FILM_CONVERT(rgbe),
REGISTER_KERNEL_FILM_CONVERT(float3),
REGISTER_KERNEL_FILM_CONVERT(motion),
REGISTER_KERNEL_FILM_CONVERT(cryptomatte),
REGISTER_KERNEL_FILM_CONVERT(shadow_catcher),
REGISTER_KERNEL_FILM_CONVERT(shadow_catcher_matte_with_shadow),
REGISTER_KERNEL_FILM_CONVERT(combined),
REGISTER_KERNEL_FILM_CONVERT(float4)
{
}
#undef REGISTER_KERNEL
#undef REGISTER_KERNEL_FILM_CONVERT
#undef KERNEL_FUNCTIONS
CCL_NAMESPACE_END

View File

@@ -0,0 +1,137 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "device/cpu/kernel_function.h"
#include "util/half.h"
CCL_NAMESPACE_BEGIN
struct ThreadKernelGlobalsCPU;
struct KernelFilmConvert;
struct IntegratorStateCPU;
struct TileInfo;
class CPUKernels {
public:
/* Integrator. */
using IntegratorFunction =
CPUKernelFunction<void (*)(const ThreadKernelGlobalsCPU *kg, IntegratorStateCPU *state)>;
using IntegratorShadeFunction = CPUKernelFunction<void (*)(const ThreadKernelGlobalsCPU *kg,
IntegratorStateCPU *state,
ccl_global float *render_buffer)>;
using IntegratorInitFunction = CPUKernelFunction<bool (*)(const ThreadKernelGlobalsCPU *kg,
IntegratorStateCPU *state,
KernelWorkTile *tile,
ccl_global float *render_buffer)>;
IntegratorInitFunction integrator_init_from_camera;
IntegratorInitFunction integrator_init_from_bake;
IntegratorShadeFunction integrator_megakernel;
/* Shader evaluation. */
using ShaderEvalFunction = CPUKernelFunction<void (*)(
const ThreadKernelGlobalsCPU *kg, const KernelShaderEvalInput *, float *, const int)>;
ShaderEvalFunction shader_eval_displace;
ShaderEvalFunction shader_eval_background;
ShaderEvalFunction shader_eval_curve_shadow_transparency;
ShaderEvalFunction shader_eval_volume_density;
/* Adaptive stopping. */
using AdaptiveSamplingConvergenceCheckFunction =
CPUKernelFunction<bool (*)(const ThreadKernelGlobalsCPU *kg,
ccl_global float *render_buffer,
const int x,
const int y,
const float threshold,
const int reset,
const int offset,
int stride)>;
using FilterXFunction = CPUKernelFunction<void (*)(const ThreadKernelGlobalsCPU *kg,
ccl_global float *render_buffer,
const int y,
const int start_x,
const int width,
const int offset,
int stride)>;
using FilterYFunction = CPUKernelFunction<void (*)(const ThreadKernelGlobalsCPU *kg,
ccl_global float *render_buffer,
const int x,
const int start_y,
const int height,
const int offset,
int stride)>;
AdaptiveSamplingConvergenceCheckFunction adaptive_sampling_convergence_check;
FilterXFunction adaptive_sampling_filter_x;
FilterYFunction adaptive_sampling_filter_y;
/* Volume Scattering Probability Guiding. */
CPUKernelFunction<void (*)(const ThreadKernelGlobalsCPU *kg,
ccl_global float *render_buffer,
const int y,
const int center_x,
const int min_x,
const int max_x,
const int offset,
int stride)>
volume_guiding_filter_x;
FilterYFunction volume_guiding_filter_y;
/* Cryptomatte. */
using CryptomattePostprocessFunction = CPUKernelFunction<void (*)(
const ThreadKernelGlobalsCPU *kg, ccl_global float *render_buffer, const int pixel_index)>;
CryptomattePostprocessFunction cryptomatte_postprocess;
/* Film Convert. */
using FilmConvertFunction = CPUKernelFunction<void (*)(const KernelFilmConvert *kfilm_convert,
const float *buffer,
float *pixel,
const int width,
const int buffer_stride,
const int pixel_stride)>;
using FilmConvertHalfRGBAFunction =
CPUKernelFunction<void (*)(const KernelFilmConvert *kfilm_convert,
const float *buffer,
half4 *pixel,
const int width,
const int buffer_stride)>;
#define KERNEL_FILM_CONVERT_FUNCTION(name) \
FilmConvertFunction film_convert_##name; \
FilmConvertHalfRGBAFunction film_convert_half_rgba_##name;
KERNEL_FILM_CONVERT_FUNCTION(depth)
KERNEL_FILM_CONVERT_FUNCTION(mist)
KERNEL_FILM_CONVERT_FUNCTION(volume_majorant)
KERNEL_FILM_CONVERT_FUNCTION(sample_count)
KERNEL_FILM_CONVERT_FUNCTION(float)
KERNEL_FILM_CONVERT_FUNCTION(light_path)
KERNEL_FILM_CONVERT_FUNCTION(rgbe)
KERNEL_FILM_CONVERT_FUNCTION(float3)
KERNEL_FILM_CONVERT_FUNCTION(motion)
KERNEL_FILM_CONVERT_FUNCTION(cryptomatte)
KERNEL_FILM_CONVERT_FUNCTION(shadow_catcher)
KERNEL_FILM_CONVERT_FUNCTION(shadow_catcher_matte_with_shadow)
KERNEL_FILM_CONVERT_FUNCTION(combined)
KERNEL_FILM_CONVERT_FUNCTION(float4)
#undef KERNEL_FILM_CONVERT_FUNCTION
CPUKernels();
};
CCL_NAMESPACE_END

View File

@@ -0,0 +1,71 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "util/debug.h" // IWYU pragma: keep
#include "util/system.h" // IWYU pragma: keep
CCL_NAMESPACE_BEGIN
/* A wrapper around per-microarchitecture variant of a kernel function.
*
* Provides a function-call-like API which gets routed to the most suitable implementation.
*
* For example, on a computer which only has AVX2 the kernel_avx2 will be used. */
template<typename FunctionType> class CPUKernelFunction {
public:
CPUKernelFunction(FunctionType kernel_default, FunctionType kernel_avx2)
{
kernel_info_ = get_best_kernel_info(kernel_default, kernel_avx2);
}
template<typename... Args> auto operator()(Args... args) const
{
assert(kernel_info_.kernel);
return kernel_info_.kernel(args...);
}
const char *get_uarch_name() const
{
return kernel_info_.uarch_name;
}
protected:
/* Helper class which allows to pass human-readable microarchitecture name together with function
* pointer. */
class KernelInfo {
public:
KernelInfo() : KernelInfo("", nullptr) {}
/* TODO(sergey): Use string view, to have higher-level functionality (i.e. comparison) without
* memory allocation. */
KernelInfo(const char *uarch_name, FunctionType kernel)
: uarch_name(uarch_name), kernel(kernel)
{
}
const char *uarch_name;
FunctionType kernel;
};
KernelInfo get_best_kernel_info(FunctionType kernel_default, FunctionType kernel_avx2)
{
/* Silence warnings about unused variables when compiling without some architectures. */
(void)kernel_avx2;
#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) {
return KernelInfo("AVX2", kernel_avx2);
}
#endif
return KernelInfo("default", kernel_default);
}
KernelInfo kernel_info_;
};
CCL_NAMESPACE_END