mirror of
https://github.com/blender/blender
synced 2026-10-25 03:07:01 +03:00
This helps avoid out of memory errors for complex scenes, and improves performance for smaller scenes with more memory available for states. Metal already had logic like this, now the logic is centralized and can be used for all GPU backends. The parameters have been somewhat tuned per device, based on earlier work for oneAPI in #163437 and CUDA in #163532. For Metal the behavior should remain basically the same. For oneAPI, this enables free_memory queries on iGPUs, as driver have been exposing this for some time. Co-authored-by: Patrick Mours <pmours@nvidia.com> Co-authored-by; Xavier Hallade <xavier.hallade@intel.com> Pull Request: https://projects.blender.org/blender/blender/pulls/163930
250 lines
6.8 KiB
C++
250 lines
6.8 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#ifdef WITH_HIP
|
|
|
|
# include "device/hip/queue.h"
|
|
|
|
# include "device/hip/device_impl.h"
|
|
# include "device/hip/graphics_interop.h"
|
|
# include "device/hip/kernel.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* HIPDeviceQueue */
|
|
|
|
HIPDeviceQueue::HIPDeviceQueue(HIPDevice *device)
|
|
: DeviceQueue(device), hip_device_(device), hip_stream_(nullptr)
|
|
{
|
|
const HIPContextScope scope(hip_device_);
|
|
hip_device_assert(hip_device_, hipStreamCreateWithFlags(&hip_stream_, hipStreamNonBlocking));
|
|
}
|
|
|
|
HIPDeviceQueue::~HIPDeviceQueue()
|
|
{
|
|
const HIPContextScope scope(hip_device_);
|
|
hipStreamDestroy(hip_stream_);
|
|
}
|
|
|
|
ConcurrentStatesParams HIPDeviceQueue::concurrent_states_params() const
|
|
{
|
|
const int max_num_threads = hip_device_->get_num_multiprocessors() *
|
|
hip_device_->get_max_num_threads_per_multiprocessor();
|
|
|
|
/* Same as CUDA. */
|
|
ConcurrentStatesParams params;
|
|
params.baseline = max(max_num_threads, 65536) * 16;
|
|
params.min = params.baseline / 2;
|
|
params.max = params.baseline * 4;
|
|
return params;
|
|
}
|
|
|
|
void HIPDeviceQueue::get_memory_info(size_t &total, size_t &free) const
|
|
{
|
|
hip_device_->get_device_memory_info(total, free);
|
|
}
|
|
|
|
void HIPDeviceQueue::init_execution()
|
|
{
|
|
/* Synchronize all textures and memory copies before executing task. */
|
|
HIPContextScope scope(hip_device_);
|
|
hip_device_->load_image_info(nullptr);
|
|
hip_device_assert(hip_device_, hipDeviceSynchronize());
|
|
|
|
debug_init_execution();
|
|
}
|
|
|
|
void HIPDeviceQueue::load_image_info()
|
|
{
|
|
HIPContextScope scope(hip_device_);
|
|
hip_device_->load_image_info(this);
|
|
}
|
|
|
|
bool HIPDeviceQueue::enqueue(DeviceKernel kernel,
|
|
const int work_size,
|
|
const DeviceKernelArguments &args)
|
|
{
|
|
if (hip_device_->have_error()) {
|
|
return false;
|
|
}
|
|
|
|
debug_enqueue_begin(kernel, work_size);
|
|
|
|
const HIPContextScope scope(hip_device_);
|
|
|
|
/* Update image info in case memory moved to host. */
|
|
if (hip_device_->load_image_info(nullptr)) {
|
|
hip_device_assert(hip_device_, hipDeviceSynchronize());
|
|
if (hip_device_->have_error()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Compute kernel launch parameters. */
|
|
const HIPDeviceKernel &hip_kernel = hip_device_->kernels.get(kernel);
|
|
const int num_threads_per_block = hip_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(hipModuleLaunchKernel(hip_kernel.function,
|
|
num_blocks,
|
|
1,
|
|
1,
|
|
num_threads_per_block,
|
|
1,
|
|
1,
|
|
shared_mem_bytes,
|
|
hip_stream_,
|
|
const_cast<void **>(args.values),
|
|
nullptr),
|
|
"enqueue");
|
|
|
|
debug_enqueue_end();
|
|
|
|
return !(hip_device_->have_error());
|
|
}
|
|
|
|
bool HIPDeviceQueue::synchronize()
|
|
{
|
|
if (hip_device_->have_error()) {
|
|
return false;
|
|
}
|
|
|
|
const HIPContextScope scope(hip_device_);
|
|
assert_success(hipStreamSynchronize(hip_stream_), "synchronize");
|
|
debug_synchronize();
|
|
|
|
return !(hip_device_->have_error());
|
|
}
|
|
|
|
void HIPDeviceQueue::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) {
|
|
hip_device_->global_alloc(mem);
|
|
}
|
|
else {
|
|
hip_device_->mem_alloc(mem);
|
|
}
|
|
}
|
|
|
|
/* Zero memory on device. */
|
|
device_ptr d_ptr = mem.device->mem_device_ptr(mem, hip_device_);
|
|
assert(d_ptr != 0);
|
|
|
|
const HIPContextScope scope(hip_device_);
|
|
assert_success(hipMemsetD8Async((hipDeviceptr_t)d_ptr, 0, mem.memory_size(), hip_stream_),
|
|
"zero_to_device");
|
|
}
|
|
|
|
void HIPDeviceQueue::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) {
|
|
hip_device_->global_alloc(mem);
|
|
}
|
|
else {
|
|
hip_device_->mem_alloc(mem);
|
|
}
|
|
}
|
|
|
|
device_ptr d_ptr = mem.device->mem_device_ptr(mem, hip_device_);
|
|
assert(d_ptr != 0);
|
|
assert(mem.host_pointer != nullptr);
|
|
|
|
/* Copy memory to device. */
|
|
const HIPContextScope scope(hip_device_);
|
|
assert_success(
|
|
hipMemcpyHtoDAsync((hipDeviceptr_t)d_ptr, mem.host_pointer, mem.memory_size(), hip_stream_),
|
|
"copy_to_device");
|
|
}
|
|
|
|
void HIPDeviceQueue::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 HIPContextScope scope(hip_device_);
|
|
assert_success(
|
|
hipMemcpyDtoHAsync(
|
|
mem.host_pointer, (hipDeviceptr_t)mem.device_pointer, mem.memory_size(), hip_stream_),
|
|
"copy_from_device");
|
|
}
|
|
|
|
void *HIPDeviceQueue::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, hip_device_);
|
|
assert(d_ptr != 0);
|
|
|
|
const HIPContextScope scope(hip_device_);
|
|
assert_success(
|
|
hipMemcpyDtoHAsync(storage.data(), (hipDeviceptr_t)d_ptr, mem.memory_size(), hip_stream_),
|
|
"copy_from_device_synchronized");
|
|
|
|
synchronize();
|
|
return storage.data();
|
|
}
|
|
|
|
void HIPDeviceQueue::assert_success(hipError_t result, const char *operation)
|
|
{
|
|
if (result != hipSuccess) {
|
|
const char *name = hipewErrorString(result);
|
|
hip_device_->set_error(
|
|
string_printf("%s in HIP queue %s (%s)", name, operation, debug_active_kernels().c_str()));
|
|
}
|
|
}
|
|
|
|
unique_ptr<DeviceGraphicsInterop> HIPDeviceQueue::graphics_interop_create()
|
|
{
|
|
return make_unique<HIPDeviceGraphicsInterop>(this);
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|
|
|
|
#endif /* WITH_HIP */
|