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,360 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "device/cuda/device.h"
#include "device/device.h"
#include "util/log.h"
#ifdef WITH_CUDA
# include "device/cuda/device_impl.h"
# include "integrator/denoiser_oidn_gpu.h" // IWYU pragma: keep
# include "util/string.h"
# ifdef _WIN32
# include "util/windows.h"
# endif
#endif /* WITH_CUDA */
CCL_NAMESPACE_BEGIN
bool device_cuda_init()
{
#if !defined(WITH_CUDA)
return false;
#elif defined(WITH_CUDA_DYNLOAD)
static bool initialized = false;
static bool result = false;
if (initialized) {
return result;
}
initialized = true;
int cuew_result = cuewInit(CUEW_INIT_CUDA);
if (cuew_result == CUEW_SUCCESS) {
LOG_INFO << "CUEW initialization succeeded";
if (CUDADevice::have_precompiled_kernels()) {
LOG_INFO << "Found precompiled kernels";
result = true;
}
else if (cuewCompilerPath() != nullptr) {
LOG_INFO << "Found CUDA compiler " << cuewCompilerPath();
result = true;
}
else {
LOG_INFO << "Neither precompiled kernels nor CUDA compiler was found,"
<< " unable to use CUDA";
}
}
else {
LOG_WARNING << "CUEW initialization failed: "
<< ((cuew_result == CUEW_ERROR_ATEXIT_FAILED) ?
"Error setting up atexit() handler" :
"Error opening the library");
}
return result;
#else /* WITH_CUDA_DYNLOAD */
return true;
#endif /* WITH_CUDA_DYNLOAD */
}
unique_ptr<Device> device_cuda_create(const DeviceInfo &info,
Stats &stats,
Profiler &profiler,
bool headless)
{
#ifdef WITH_CUDA
return make_unique<CUDADevice>(info, stats, profiler, headless);
#else
(void)info;
(void)stats;
(void)profiler;
(void)headless;
LOG_FATAL << "Request to create CUDA device without compiled-in support. Should never happen.";
return nullptr;
#endif
}
#ifdef WITH_CUDA
static CUresult device_cuda_safe_init()
{
# ifdef _WIN32
__try
{
return cuInit(0);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
/* Ignore crashes inside the CUDA driver and hope we can
* survive even with corrupted CUDA installs. */
fprintf(stderr, "Cycles CUDA: driver crashed, continuing without CUDA.\n");
}
return CUDA_ERROR_NO_DEVICE;
# else
return cuInit(0);
# endif
}
#endif /* WITH_CUDA */
void device_cuda_info(vector<DeviceInfo> &devices)
{
#ifdef WITH_CUDA
CUresult result = device_cuda_safe_init();
if (result != CUDA_SUCCESS) {
if (result != CUDA_ERROR_NO_DEVICE) {
LOG_ERROR << "CUDA cuInit: " << cuewErrorString(result);
}
return;
}
int count = 0;
result = cuDeviceGetCount(&count);
if (result != CUDA_SUCCESS) {
LOG_ERROR << "CUDA cuDeviceGetCount: " << cuewErrorString(result);
return;
}
vector<DeviceInfo> display_devices;
for (int num = 0; num < count; num++) {
char name[256];
result = cuDeviceGetName(name, 256, num);
if (result != CUDA_SUCCESS) {
LOG_ERROR << "CUDA cuDeviceGetName: " << cuewErrorString(result);
continue;
}
if (!cudaSupportsDevice(num)) {
LOG_INFO << "Ignoring device \"" << name << "\", this graphics card is no longer supported.";
continue;
}
DeviceInfo info;
info.type = DEVICE_CUDA;
info.description = string(name);
info.num = num;
info.has_nanovdb = true;
info.denoisers = 0;
info.has_gpu_queue = true;
/* Check if the device has P2P access to any other device in the system. */
for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) {
if (num != peer_num) {
if (cudaSupportsDevice(peer_num)) {
int can_access = 0;
cuDeviceCanAccessPeer(&can_access, num, peer_num);
info.has_peer_memory = (can_access != 0);
}
}
}
int pci_location[3] = {0, 0, 0};
cuDeviceGetAttribute(&pci_location[0], CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID, num);
cuDeviceGetAttribute(&pci_location[1], CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, num);
cuDeviceGetAttribute(&pci_location[2], CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID, num);
info.id = string_printf("CUDA_%s_%04x:%02x:%02x",
name,
(unsigned int)pci_location[0],
(unsigned int)pci_location[1],
(unsigned int)pci_location[2]);
# if defined(WITH_OPENIMAGEDENOISE)
# if OIDN_VERSION >= 20300
if (oidnIsCUDADeviceSupported(num)) {
# else
if (OIDNDenoiserGPU::is_device_supported(info)) {
# endif
info.denoisers |= DENOISER_OPENIMAGEDENOISE;
}
# endif
/* If device has a kernel timeout and no compute preemption, we assume
* it is connected to a display and will freeze the display while doing
* computations. */
int timeout_attr = 0, preempt_attr = 0;
cuDeviceGetAttribute(&timeout_attr, CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT, num);
cuDeviceGetAttribute(&preempt_attr, CU_DEVICE_ATTRIBUTE_COMPUTE_PREEMPTION_SUPPORTED, num);
# ifdef _WIN32
int major;
cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, num);
/* The CUDA driver reports compute preemption as not being available on
* Windows 10 even when it is, due to an issue in application profiles.
* Detect case where we expect it to be available and override. */
if (preempt_attr == 0 && (major >= 6) && system_windows_version_at_least(10, 17134)) {
LOG_INFO << "Assuming device has compute preemption on Windows 10.";
preempt_attr = 1;
}
# endif
if (timeout_attr && !preempt_attr) {
LOG_INFO << "Device is recognized as display.";
info.description += " (Display)";
info.display_device = true;
display_devices.push_back(info);
}
else {
LOG_INFO << "Device has compute preemption or is not used for display.";
devices.push_back(info);
}
LOG_INFO << "Added device \"" << info.description << "\" with id \"" << info.id << "\".";
if (info.denoisers & DENOISER_OPENIMAGEDENOISE) {
LOG_INFO << "Device with id \"" << info.id << "\" supports "
<< denoiserTypeToHumanReadable(DENOISER_OPENIMAGEDENOISE) << ".";
}
}
if (!display_devices.empty()) {
devices.insert(devices.end(), display_devices.begin(), display_devices.end());
}
#else /* WITH_CUDA */
(void)devices;
#endif /* WITH_CUDA */
}
string device_cuda_capabilities()
{
#ifdef WITH_CUDA
CUresult result = device_cuda_safe_init();
if (result != CUDA_SUCCESS) {
if (result != CUDA_ERROR_NO_DEVICE) {
return string("Error initializing CUDA: ") + cuewErrorString(result);
}
return "No CUDA device found\n";
}
int count;
result = cuDeviceGetCount(&count);
if (result != CUDA_SUCCESS) {
return string("Error getting devices: ") + cuewErrorString(result);
}
string capabilities;
for (int num = 0; num < count; num++) {
char name[256];
if (cuDeviceGetName(name, 256, num) != CUDA_SUCCESS) {
continue;
}
capabilities += string("\t") + name + "\n";
int value;
# define GET_ATTR(attr) \
{ \
if (cuDeviceGetAttribute(&value, CU_DEVICE_ATTRIBUTE_##attr, num) == CUDA_SUCCESS) { \
capabilities += string_printf("\t\tCU_DEVICE_ATTRIBUTE_" #attr "\t\t\t%d\n", value); \
} \
} \
(void)0
/* TODO(sergey): Strip all attributes which are not useful for us
* or does not depend on the driver.
*/
GET_ATTR(MAX_THREADS_PER_BLOCK);
GET_ATTR(MAX_BLOCK_DIM_X);
GET_ATTR(MAX_BLOCK_DIM_Y);
GET_ATTR(MAX_BLOCK_DIM_Z);
GET_ATTR(MAX_GRID_DIM_X);
GET_ATTR(MAX_GRID_DIM_Y);
GET_ATTR(MAX_GRID_DIM_Z);
GET_ATTR(MAX_SHARED_MEMORY_PER_BLOCK);
GET_ATTR(SHARED_MEMORY_PER_BLOCK);
GET_ATTR(TOTAL_CONSTANT_MEMORY);
GET_ATTR(WARP_SIZE);
GET_ATTR(MAX_PITCH);
GET_ATTR(MAX_REGISTERS_PER_BLOCK);
GET_ATTR(REGISTERS_PER_BLOCK);
GET_ATTR(CLOCK_RATE);
GET_ATTR(TEXTURE_ALIGNMENT);
GET_ATTR(GPU_OVERLAP);
GET_ATTR(MULTIPROCESSOR_COUNT);
GET_ATTR(KERNEL_EXEC_TIMEOUT);
GET_ATTR(INTEGRATED);
GET_ATTR(CAN_MAP_HOST_MEMORY);
GET_ATTR(COMPUTE_MODE);
GET_ATTR(MAXIMUM_TEXTURE1D_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_HEIGHT);
GET_ATTR(MAXIMUM_TEXTURE3D_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE3D_HEIGHT);
GET_ATTR(MAXIMUM_TEXTURE3D_DEPTH);
GET_ATTR(MAXIMUM_TEXTURE2D_LAYERED_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_LAYERED_HEIGHT);
GET_ATTR(MAXIMUM_TEXTURE2D_LAYERED_LAYERS);
GET_ATTR(MAXIMUM_TEXTURE2D_ARRAY_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_ARRAY_HEIGHT);
GET_ATTR(MAXIMUM_TEXTURE2D_ARRAY_NUMSLICES);
GET_ATTR(SURFACE_ALIGNMENT);
GET_ATTR(CONCURRENT_KERNELS);
GET_ATTR(ECC_ENABLED);
GET_ATTR(TCC_DRIVER);
GET_ATTR(MEMORY_CLOCK_RATE);
GET_ATTR(GLOBAL_MEMORY_BUS_WIDTH);
GET_ATTR(L2_CACHE_SIZE);
GET_ATTR(MAX_THREADS_PER_MULTIPROCESSOR);
GET_ATTR(ASYNC_ENGINE_COUNT);
GET_ATTR(UNIFIED_ADDRESSING);
GET_ATTR(MAXIMUM_TEXTURE1D_LAYERED_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE1D_LAYERED_LAYERS);
GET_ATTR(CAN_TEX2D_GATHER);
GET_ATTR(MAXIMUM_TEXTURE2D_GATHER_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_GATHER_HEIGHT);
GET_ATTR(MAXIMUM_TEXTURE3D_WIDTH_ALTERNATE);
GET_ATTR(MAXIMUM_TEXTURE3D_HEIGHT_ALTERNATE);
GET_ATTR(MAXIMUM_TEXTURE3D_DEPTH_ALTERNATE);
GET_ATTR(TEXTURE_PITCH_ALIGNMENT);
GET_ATTR(MAXIMUM_TEXTURECUBEMAP_WIDTH);
GET_ATTR(MAXIMUM_TEXTURECUBEMAP_LAYERED_WIDTH);
GET_ATTR(MAXIMUM_TEXTURECUBEMAP_LAYERED_LAYERS);
GET_ATTR(MAXIMUM_SURFACE1D_WIDTH);
GET_ATTR(MAXIMUM_SURFACE2D_WIDTH);
GET_ATTR(MAXIMUM_SURFACE2D_HEIGHT);
GET_ATTR(MAXIMUM_SURFACE3D_WIDTH);
GET_ATTR(MAXIMUM_SURFACE3D_HEIGHT);
GET_ATTR(MAXIMUM_SURFACE3D_DEPTH);
GET_ATTR(MAXIMUM_SURFACE1D_LAYERED_WIDTH);
GET_ATTR(MAXIMUM_SURFACE1D_LAYERED_LAYERS);
GET_ATTR(MAXIMUM_SURFACE2D_LAYERED_WIDTH);
GET_ATTR(MAXIMUM_SURFACE2D_LAYERED_HEIGHT);
GET_ATTR(MAXIMUM_SURFACE2D_LAYERED_LAYERS);
GET_ATTR(MAXIMUM_SURFACECUBEMAP_WIDTH);
GET_ATTR(MAXIMUM_SURFACECUBEMAP_LAYERED_WIDTH);
GET_ATTR(MAXIMUM_SURFACECUBEMAP_LAYERED_LAYERS);
GET_ATTR(MAXIMUM_TEXTURE1D_LINEAR_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_LINEAR_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_LINEAR_HEIGHT);
GET_ATTR(MAXIMUM_TEXTURE2D_LINEAR_PITCH);
GET_ATTR(MAXIMUM_TEXTURE2D_MIPMAPPED_WIDTH);
GET_ATTR(MAXIMUM_TEXTURE2D_MIPMAPPED_HEIGHT);
GET_ATTR(COMPUTE_CAPABILITY_MAJOR);
GET_ATTR(COMPUTE_CAPABILITY_MINOR);
GET_ATTR(MAXIMUM_TEXTURE1D_MIPMAPPED_WIDTH);
GET_ATTR(STREAM_PRIORITIES_SUPPORTED);
GET_ATTR(GLOBAL_L1_CACHE_SUPPORTED);
GET_ATTR(LOCAL_L1_CACHE_SUPPORTED);
GET_ATTR(MAX_SHARED_MEMORY_PER_MULTIPROCESSOR);
GET_ATTR(MAX_REGISTERS_PER_MULTIPROCESSOR);
GET_ATTR(MANAGED_MEMORY);
GET_ATTR(MULTI_GPU_BOARD);
GET_ATTR(MULTI_GPU_BOARD_GROUP_ID);
# undef GET_ATTR
capabilities += "\n";
}
return capabilities;
#else /* WITH_CUDA */
return "";
#endif /* WITH_CUDA */
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,29 @@
/* 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;
bool device_cuda_init();
unique_ptr<Device> device_cuda_create(const DeviceInfo &info,
Stats &stats,
Profiler &profiler,
bool headless);
void device_cuda_info(vector<DeviceInfo> &devices);
string device_cuda_capabilities();
CCL_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_CUDA
# include "device/cuda/kernel.h"
# include "device/cuda/queue.h"
# include "device/cuda/util.h"
# include "device/device.h"
# ifdef WITH_CUDA_DYNLOAD
# include "cuew.h"
# else
# include <cuda.h>
# include <cudaGL.h>
# endif
CCL_NAMESPACE_BEGIN
class DeviceQueue;
class CUDADevice : public GPUDevice {
friend class CUDAContextScope;
public:
CUdevice cuDevice;
CUcontext cuContext;
CUmodule cuModule;
int pitch_alignment;
int cuDevId;
int cuDevArchitecture;
bool first_error;
CUDADeviceKernels kernels;
static bool have_precompiled_kernels();
BVHLayoutMask get_bvh_layout_mask(uint /*kernel_features*/) const override;
void set_error(const string &error) override;
CUDADevice(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless);
~CUDADevice() override;
bool support_device(const uint /*kernel_features*/);
bool check_peer_access(Device *peer_device) override;
bool use_adaptive_compilation();
string compile_kernel_get_common_cflags(const uint kernel_features);
string compile_kernel(const string &cflags, const char *name, bool optix = false);
bool load_kernels(const uint kernel_features) override;
void reserve_local_memory(const uint kernel_features);
/* All memory types. */
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;
device_ptr mem_alloc_sub_ptr(device_memory &mem, const size_t offset, size_t /*size*/) override;
/* Global memory. */
void global_alloc(device_memory &mem);
void global_copy_to(device_memory &mem);
void global_free(device_memory &mem);
/* Image memory. */
void image_alloc(device_image &mem);
void image_copy_to(device_image &mem);
void image_free(device_image &mem);
/* Device side memory. */
void get_device_memory_info(size_t &total, size_t &free) override;
bool alloc_device(void *&device_pointer, const size_t size) override;
void free_device(void *device_pointer) override;
/* Shared memory. */
bool shared_alloc(void *&shared_pointer, const size_t size) override;
void shared_free(void *shared_pointer) override;
void *shared_to_device_pointer(const void *shared_pointer) override;
/* Memory copy. */
void copy_host_to_device(void *device_pointer, void *host_pointer, const size_t size) override;
void const_copy_to(const char *name, void *host, const size_t size) override;
bool should_use_graphics_interop(const GraphicsInteropDevice &interop_device,
const bool log) override;
unique_ptr<DeviceQueue> gpu_queue_create() override;
int get_num_multiprocessors();
int get_max_num_threads_per_multiprocessor();
protected:
bool get_device_attribute(CUdevice_attribute attribute, int *value);
int get_device_default_attribute(CUdevice_attribute attribute, const int default_value);
};
CCL_NAMESPACE_END
#endif

View File

@@ -0,0 +1,186 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_CUDA
# include "device/cuda/graphics_interop.h"
# include "device/cuda/device_impl.h"
# include "device/cuda/util.h"
# include "session/display_driver.h"
# ifdef _WIN32
# include "util/windows.h"
# else
# include <unistd.h>
# endif
CCL_NAMESPACE_BEGIN
CUDADeviceGraphicsInterop::CUDADeviceGraphicsInterop(CUDADeviceQueue *queue)
: queue_(queue), device_(static_cast<CUDADevice *>(queue->device))
{
}
CUDADeviceGraphicsInterop::~CUDADeviceGraphicsInterop()
{
CUDAContextScope scope(device_);
free();
}
void CUDADeviceGraphicsInterop::set_buffer(GraphicsInteropBuffer &interop_buffer)
{
CUDAContextScope scope(device_);
if (interop_buffer.is_empty()) {
free();
return;
}
need_zero_ |= interop_buffer.take_zero();
if (!interop_buffer.has_new_handle()) {
return;
}
free();
switch (interop_buffer.get_type()) {
case GraphicsInteropDevice::OPENGL: {
const CUresult result = cuGraphicsGLRegisterBuffer(&cu_graphics_resource_,
interop_buffer.take_handle(),
CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
if (result != CUDA_SUCCESS) {
LOG_ERROR << "Error registering OpenGL buffer: " << cuewErrorString(result);
break;
}
buffer_size_ = interop_buffer.get_size();
break;
}
case GraphicsInteropDevice::VULKAN: {
CUDA_EXTERNAL_MEMORY_HANDLE_DESC external_memory_handle_desc = {};
# ifdef _WIN32
/* cuImportExternalMemory will not take ownership of the handle. */
vulkan_windows_handle_ = interop_buffer.take_handle();
external_memory_handle_desc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
external_memory_handle_desc.handle.win32.handle = reinterpret_cast<void *>(
vulkan_windows_handle_);
# else
/* cuImportExternalMemory will take ownership of the handle. */
external_memory_handle_desc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD;
external_memory_handle_desc.handle.fd = interop_buffer.take_handle();
# endif
external_memory_handle_desc.size = interop_buffer.get_size();
CUresult result = cuImportExternalMemory(&cu_external_memory_, &external_memory_handle_desc);
if (result != CUDA_SUCCESS) {
# ifdef _WIN32
CloseHandle(HANDLE(vulkan_windows_handle_));
vulkan_windows_handle_ = 0;
# else
close(external_memory_handle_desc.handle.fd);
# endif
LOG_ERROR << "Error importing Vulkan memory: " << cuewErrorString(result);
break;
}
buffer_size_ = interop_buffer.get_size();
CUDA_EXTERNAL_MEMORY_BUFFER_DESC external_memory_buffer_desc = {};
external_memory_buffer_desc.size = external_memory_handle_desc.size;
external_memory_buffer_desc.offset = 0;
CUdeviceptr external_memory_device_ptr = 0;
result = cuExternalMemoryGetMappedBuffer(
&external_memory_device_ptr, cu_external_memory_, &external_memory_buffer_desc);
if (result != CUDA_SUCCESS) {
if (external_memory_device_ptr) {
cuMemFree(external_memory_device_ptr);
external_memory_device_ptr = 0;
}
LOG_ERROR << "Error mapping Vulkan memory: " << cuewErrorString(result);
break;
}
cu_external_memory_ptr_ = external_memory_device_ptr;
break;
}
case GraphicsInteropDevice::METAL:
case GraphicsInteropDevice::NONE:
break;
}
}
device_ptr CUDADeviceGraphicsInterop::map()
{
CUdeviceptr cu_buffer = 0;
if (cu_graphics_resource_) {
/* OpenGL buffer needs mapping. */
CUDAContextScope scope(device_);
size_t bytes;
cuda_device_assert(device_,
cuGraphicsMapResources(1, &cu_graphics_resource_, queue_->stream()));
cuda_device_assert(
device_, cuGraphicsResourceGetMappedPointer(&cu_buffer, &bytes, cu_graphics_resource_));
}
else {
/* Vulkan buffer is always mapped. */
cu_buffer = cu_external_memory_ptr_;
}
if (cu_buffer && need_zero_) {
cuda_device_assert(device_, cuMemsetD8Async(cu_buffer, 0, buffer_size_, queue_->stream()));
need_zero_ = false;
}
return static_cast<device_ptr>(cu_buffer);
}
void CUDADeviceGraphicsInterop::unmap()
{
if (cu_graphics_resource_) {
CUDAContextScope scope(device_);
cuda_device_assert(device_,
cuGraphicsUnmapResources(1, &cu_graphics_resource_, queue_->stream()));
}
}
void CUDADeviceGraphicsInterop::free()
{
if (cu_graphics_resource_) {
cuda_device_assert(device_, cuGraphicsUnregisterResource(cu_graphics_resource_));
cu_graphics_resource_ = nullptr;
}
if (cu_external_memory_ptr_) {
cuda_device_assert(device_, cuMemFree(cu_external_memory_ptr_));
cu_external_memory_ptr_ = 0;
}
if (cu_external_memory_) {
cuda_device_assert(device_, cuDestroyExternalMemory(cu_external_memory_));
cu_external_memory_ = nullptr;
}
# ifdef _WIN32
if (vulkan_windows_handle_) {
CloseHandle(HANDLE(vulkan_windows_handle_));
vulkan_windows_handle_ = 0;
}
# endif
buffer_size_ = 0;
need_zero_ = false;
}
CCL_NAMESPACE_END
#endif

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_CUDA
# include "device/graphics_interop.h"
# include "session/display_driver.h"
# ifdef WITH_CUDA_DYNLOAD
# include "cuew.h"
# else
# include <cuda.h>
# endif
CCL_NAMESPACE_BEGIN
class CUDADevice;
class CUDADeviceQueue;
class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop {
public:
explicit CUDADeviceGraphicsInterop(CUDADeviceQueue *queue);
CUDADeviceGraphicsInterop(const CUDADeviceGraphicsInterop &other) = delete;
CUDADeviceGraphicsInterop(CUDADeviceGraphicsInterop &&other) noexcept = delete;
~CUDADeviceGraphicsInterop() override;
CUDADeviceGraphicsInterop &operator=(const CUDADeviceGraphicsInterop &other) = delete;
CUDADeviceGraphicsInterop &operator=(CUDADeviceGraphicsInterop &&other) = delete;
void set_buffer(GraphicsInteropBuffer &interop_buffer) override;
device_ptr map() override;
void unmap() override;
protected:
CUDADeviceQueue *queue_ = nullptr;
CUDADevice *device_ = nullptr;
/* Size of the buffer in bytes. */
size_t buffer_size_ = 0;
/* The destination was requested to be cleared. */
bool need_zero_ = false;
/* CUDA resources. */
CUgraphicsResource cu_graphics_resource_ = nullptr;
CUexternalMemory cu_external_memory_ = nullptr;
CUdeviceptr cu_external_memory_ptr_ = 0;
/* Vulkan handle to free. */
# ifdef _WIN32
int64_t vulkan_windows_handle_ = 0;
# endif
void free();
};
CCL_NAMESPACE_END
#endif

View File

@@ -0,0 +1,56 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_CUDA
# include "device/cuda/kernel.h"
# include "device/cuda/device_impl.h"
CCL_NAMESPACE_BEGIN
void CUDADeviceKernels::load(CUDADevice *device)
{
CUmodule cuModule = device->cuModule;
for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) {
CUDADeviceKernel &kernel = kernels_[i];
if (!device_kernel_has_gpu_function((DeviceKernel)i)) {
continue;
}
const std::string function_name = std::string("kernel_gpu_") +
device_kernel_as_string((DeviceKernel)i);
cuda_device_assert(device,
cuModuleGetFunction(&kernel.function, cuModule, function_name.c_str()));
if (kernel.function) {
cuda_device_assert(device, cuFuncSetCacheConfig(kernel.function, CU_FUNC_CACHE_PREFER_L1));
cuda_device_assert(
device,
cuOccupancyMaxPotentialBlockSize(
&kernel.min_blocks, &kernel.num_threads_per_block, kernel.function, nullptr, 0, 0));
}
else {
LOG_ERROR << "Unable to load kernel " << function_name;
}
}
loaded = true;
}
const CUDADeviceKernel &CUDADeviceKernels::get(DeviceKernel kernel) const
{
return kernels_[(int)kernel];
}
bool CUDADeviceKernels::available(DeviceKernel kernel) const
{
return kernels_[(int)kernel].function != nullptr;
}
CCL_NAMESPACE_END
#endif /* WITH_CUDA */

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#ifdef WITH_CUDA
# include "device/kernel.h"
# ifdef WITH_CUDA_DYNLOAD
# include "cuew.h"
# else
# include <cuda.h>
# endif
CCL_NAMESPACE_BEGIN
class CUDADevice;
/* CUDA kernel and associate occupancy information. */
class CUDADeviceKernel {
public:
CUfunction function = nullptr;
int num_threads_per_block = 0;
int min_blocks = 0;
};
/* Cache of CUDA kernels for each DeviceKernel. */
class CUDADeviceKernels {
public:
void load(CUDADevice *device);
const CUDADeviceKernel &get(DeviceKernel kernel) const;
bool available(DeviceKernel kernel) const;
protected:
CUDADeviceKernel kernels_[DEVICE_KERNEL_NUM];
bool loaded = false;
};
CCL_NAMESPACE_END
#endif /* WITH_CUDA */

View File

@@ -0,0 +1,271 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_CUDA
# include "device/cuda/queue.h"
# include "device/cuda/device_impl.h"
# include "device/cuda/graphics_interop.h"
# include "device/cuda/kernel.h"
CCL_NAMESPACE_BEGIN
/* CUDADeviceQueue */
CUDADeviceQueue::CUDADeviceQueue(CUDADevice *device)
: DeviceQueue(device), cuda_device_(device), cuda_stream_(nullptr)
{
const CUDAContextScope scope(cuda_device_);
cuda_device_assert(cuda_device_, cuStreamCreate(&cuda_stream_, CU_STREAM_NON_BLOCKING));
}
CUDADeviceQueue::~CUDADeviceQueue()
{
const CUDAContextScope scope(cuda_device_);
cuStreamDestroy(cuda_stream_);
}
int CUDADeviceQueue::num_concurrent_states(const size_t state_size) const
{
const int max_num_threads = cuda_device_->get_num_multiprocessors() *
cuda_device_->get_max_num_threads_per_multiprocessor();
int num_states = max(max_num_threads, 65536) * 16;
const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR");
if (factor_str) {
const float factor = (float)atof(factor_str);
if (factor != 0.0f) {
num_states = max((int)(num_states * factor), 1024);
}
else {
LOG_TRACE << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0";
}
}
LOG_TRACE << "GPU queue concurrent states: " << num_states << ", using up to "
<< string_human_readable_size(num_states * state_size);
return num_states;
}
int CUDADeviceQueue::num_concurrent_busy_states(const size_t /*state_size*/) const
{
const int max_num_threads = cuda_device_->get_num_multiprocessors() *
cuda_device_->get_max_num_threads_per_multiprocessor();
if (max_num_threads == 0) {
return 65536;
}
return 4 * max_num_threads;
}
void CUDADeviceQueue::init_execution()
{
/* Synchronize all textures and memory copies before executing task.
* Use default stream (nullptr) since that's what we will synchronize
* here to ensure all scene data is copied. */
CUDAContextScope scope(cuda_device_);
cuda_device_->load_image_info(nullptr);
cuda_device_assert(cuda_device_, cuCtxSynchronize());
debug_init_execution();
}
void CUDADeviceQueue::load_image_info()
{
CUDAContextScope scope(cuda_device_);
cuda_device_->load_image_info(this);
}
bool CUDADeviceQueue::enqueue(DeviceKernel kernel,
const int work_size,
const DeviceKernelArguments &args)
{
if (cuda_device_->have_error()) {
return false;
}
debug_enqueue_begin(kernel, work_size);
const CUDAContextScope scope(cuda_device_);
/* Update image info in case integrator memory alloc caused texture to move to host. */
if (cuda_device_->load_image_info(nullptr)) {
cuda_device_assert(cuda_device_, cuCtxSynchronize());
if (cuda_device_->have_error()) {
return false;
}
}
/* Compute kernel launch parameters. */
const CUDADeviceKernel &cuda_kernel = cuda_device_->kernels.get(kernel);
const int num_threads_per_block = cuda_kernel.num_threads_per_block;
const int num_blocks = divide_up(work_size, num_threads_per_block);
int shared_mem_bytes = 0;
switch (kernel) {
case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY:
case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY:
/* See parall_active_index.h for why this amount of shared memory is needed. */
shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int);
break;
default:
break;
}
/* Launch kernel. */
assert_success(cuLaunchKernel(cuda_kernel.function,
num_blocks,
1,
1,
num_threads_per_block,
1,
1,
shared_mem_bytes,
cuda_stream_,
const_cast<void **>(args.values),
nullptr),
"enqueue");
debug_enqueue_end();
return !(cuda_device_->have_error());
}
bool CUDADeviceQueue::synchronize()
{
if (cuda_device_->have_error()) {
return false;
}
const CUDAContextScope scope(cuda_device_);
assert_success(cuStreamSynchronize(cuda_stream_), "synchronize");
debug_synchronize();
return !(cuda_device_->have_error());
}
void CUDADeviceQueue::zero_to_device(device_memory &mem)
{
assert(mem.type != MEM_IMAGE_TEXTURE);
if (mem.memory_size() == 0) {
return;
}
/* Allocate on demand. */
if (mem.device_pointer == 0) {
if (mem.type == MEM_GLOBAL) {
cuda_device_->global_alloc(mem);
}
else {
cuda_device_->mem_alloc(mem);
}
}
/* Zero memory on device. */
device_ptr d_ptr = mem.device->mem_device_ptr(mem, cuda_device_);
assert(d_ptr != 0);
const CUDAContextScope scope(cuda_device_);
assert_success(cuMemsetD8Async((CUdeviceptr)d_ptr, 0, mem.memory_size(), cuda_stream_),
"zero_to_device");
}
void CUDADeviceQueue::copy_to_device(device_memory &mem)
{
assert(mem.type != MEM_IMAGE_TEXTURE);
if (mem.memory_size() == 0) {
return;
}
/* Allocate on demand. */
if (mem.device_pointer == 0) {
if (mem.type == MEM_GLOBAL) {
cuda_device_->global_alloc(mem);
}
else {
cuda_device_->mem_alloc(mem);
}
}
device_ptr d_ptr = mem.device->mem_device_ptr(mem, cuda_device_);
assert(d_ptr != 0);
assert(mem.host_pointer != nullptr);
/* Copy memory to device. */
const CUDAContextScope scope(cuda_device_);
assert_success(
cuMemcpyHtoDAsync((CUdeviceptr)d_ptr, mem.host_pointer, mem.memory_size(), cuda_stream_),
"copy_to_device");
}
void CUDADeviceQueue::copy_from_device(device_memory &mem)
{
assert(mem.type != MEM_GLOBAL && mem.type != MEM_IMAGE_TEXTURE);
if (mem.memory_size() == 0) {
return;
}
assert(mem.device_pointer != 0);
assert(mem.host_pointer != nullptr);
/* Copy memory from device. */
const CUDAContextScope scope(cuda_device_);
assert_success(
cuMemcpyDtoHAsync(
mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_),
"copy_from_device");
}
void *CUDADeviceQueue::copy_from_device_synchronized(device_memory &mem, vector<uint8_t> &storage)
{
if (mem.memory_size() == 0) {
return nullptr;
}
storage.resize(mem.memory_size());
device_ptr d_ptr = mem.device->mem_device_ptr(mem, cuda_device_);
assert(d_ptr != 0);
const CUDAContextScope scope(cuda_device_);
assert_success(
cuMemcpyDtoHAsync(storage.data(), (CUdeviceptr)d_ptr, mem.memory_size(), cuda_stream_),
"copy_from_device_synchronized");
synchronize();
return storage.data();
}
void CUDADeviceQueue::assert_success(CUresult result, const char *operation)
{
if (result != CUDA_SUCCESS) {
const char *name = cuewErrorString(result);
cuda_device_->set_error(string_printf(
"%s in CUDA queue %s (%s)", name, operation, debug_active_kernels().c_str()));
}
}
unique_ptr<DeviceGraphicsInterop> CUDADeviceQueue::graphics_interop_create()
{
return make_unique<CUDADeviceGraphicsInterop>(this);
}
CCL_NAMESPACE_END
#endif /* WITH_CUDA */

View File

@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#ifdef WITH_CUDA
# include "device/memory.h"
# include "device/queue.h"
# include "device/cuda/util.h"
CCL_NAMESPACE_BEGIN
class CUDADevice;
class device_memory;
/* Base class for CUDA queues. */
class CUDADeviceQueue : public DeviceQueue {
public:
CUDADeviceQueue(CUDADevice *device);
~CUDADeviceQueue() override;
int num_concurrent_states(const size_t state_size) const override;
int num_concurrent_busy_states(const size_t state_size) const override;
void init_execution() override;
void load_image_info() override;
bool enqueue(DeviceKernel kernel,
const int work_size,
const DeviceKernelArguments &args) override;
bool synchronize() override;
void zero_to_device(device_memory &mem) override;
void copy_to_device(device_memory &mem) override;
void copy_from_device(device_memory &mem) override;
void *copy_from_device_synchronized(device_memory &mem, vector<uint8_t> &storage) override;
virtual CUstream stream()
{
return cuda_stream_;
}
unique_ptr<DeviceGraphicsInterop> graphics_interop_create() override;
protected:
CUDADevice *cuda_device_;
CUstream cuda_stream_;
void assert_success(CUresult result, const char *operation);
};
CCL_NAMESPACE_END
#endif /* WITH_CUDA */

View File

@@ -0,0 +1,49 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_CUDA
# include "device/cuda/util.h"
# include "device/cuda/device_impl.h"
CCL_NAMESPACE_BEGIN
CUDAContextScope::CUDAContextScope(CUDADevice *device) : device(device)
{
cuda_device_assert(device, cuCtxPushCurrent(device->cuContext));
}
CUDAContextScope::~CUDAContextScope()
{
cuda_device_assert(device, cuCtxPopCurrent(nullptr));
}
# ifndef WITH_CUDA_DYNLOAD
const char *cuewErrorString(CUresult result)
{
/* We can only give error code here without major code duplication, that
* should be enough since dynamic loading is only being disabled by folks
* who knows what they're doing anyway.
*
* NOTE: Avoid call from several threads.
*/
static string error;
error = string_printf("%d", result);
return error.c_str();
}
const char *cuewCompilerPath()
{
return CYCLES_CUDA_NVCC_EXECUTABLE;
}
int cuewCompilerVersion()
{
return (CUDA_VERSION / 100) + (CUDA_VERSION % 100 / 10);
}
# endif
CCL_NAMESPACE_END
#endif /* WITH_CUDA */

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#ifdef WITH_CUDA
# ifdef WITH_CUDA_DYNLOAD
# include <cuew.h>
# else
# include <cuda.h>
# endif
CCL_NAMESPACE_BEGIN
class CUDADevice;
/* Utility to push/pop CUDA context. */
class CUDAContextScope {
public:
CUDAContextScope(CUDADevice *device);
~CUDAContextScope();
private:
CUDADevice *device;
};
/* Utility for checking return values of CUDA function calls. */
# define cuda_device_assert(cuda_device, stmt) \
{ \
CUresult result = stmt; \
if (result != CUDA_SUCCESS) { \
const char *name = cuewErrorString(result); \
cuda_device->set_error( \
string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \
} \
} \
(void)0
# define cuda_assert(stmt) cuda_device_assert(this, stmt)
# ifndef WITH_CUDA_DYNLOAD
/* Transparently implement some functions, so majority of the file does not need
* to worry about difference between dynamically loaded and linked CUDA at all. */
const char *cuewErrorString(CUresult result);
const char *cuewCompilerPath();
int cuewCompilerVersion();
# endif /* WITH_CUDA_DYNLOAD */
static inline bool cudaSupportsDevice(const int cudaDevID)
{
int major;
cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cudaDevID);
if (major >= 5) {
return true;
}
return false;
}
CCL_NAMESPACE_END
#endif /* WITH_CUDA */