Blender  V2.93
memory_manager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2017 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifdef WITH_OPENCL
18 
19 # include "util/util_foreach.h"
20 
23 
25 
26 void MemoryManager::DeviceBuffer::add_allocation(Allocation &allocation)
27 {
28  allocations.push_back(&allocation);
29 }
30 
31 void MemoryManager::DeviceBuffer::update_device_memory(OpenCLDevice *device)
32 {
33  bool need_realloc = false;
34 
35  /* Calculate total size and remove any freed. */
36  size_t total_size = 0;
37 
38  for (int i = allocations.size() - 1; i >= 0; i--) {
39  Allocation *allocation = allocations[i];
40 
41  /* Remove allocations that have been freed. */
42  if (!allocation->mem || allocation->mem->memory_size() == 0) {
43  allocation->device_buffer = NULL;
44  allocation->size = 0;
45 
46  allocations.erase(allocations.begin() + i);
47 
48  need_realloc = true;
49 
50  continue;
51  }
52 
53  /* Get actual size for allocation. */
54  size_t alloc_size = align_up(allocation->mem->memory_size(), 16);
55 
56  if (allocation->size != alloc_size) {
57  /* Allocation is either new or resized. */
58  allocation->size = alloc_size;
59  allocation->needs_copy_to_device = true;
60 
61  need_realloc = true;
62  }
63 
64  total_size += alloc_size;
65  }
66 
67  /* Always allocate non-empty buffer, NULL pointers cause problems with some drivers. */
68  total_size = std::max(total_size, (size_t)16);
69 
70  if (need_realloc) {
71  cl_ulong max_buffer_size;
72  clGetDeviceInfo(
73  device->cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_buffer_size, NULL);
74 
75  if (total_size > max_buffer_size) {
76  device->set_error("Scene too complex to fit in available memory.");
77  return;
78  }
79 
81  "memory manager buffer");
82 
83  new_buffer->alloc_to_device(total_size);
84 
85  size_t offset = 0;
86 
87  foreach (Allocation *allocation, allocations) {
88  if (allocation->needs_copy_to_device) {
89  /* Copy from host to device. */
90  opencl_device_assert(device,
91  clEnqueueWriteBuffer(device->cqCommandQueue,
92  CL_MEM_PTR(new_buffer->device_pointer),
93  CL_FALSE,
94  offset,
95  allocation->mem->memory_size(),
96  allocation->mem->host_pointer,
97  0,
98  NULL,
99  NULL));
100 
101  allocation->needs_copy_to_device = false;
102  }
103  else {
104  /* Fast copy from memory already on device. */
105  opencl_device_assert(device,
106  clEnqueueCopyBuffer(device->cqCommandQueue,
107  CL_MEM_PTR(buffer->device_pointer),
108  CL_MEM_PTR(new_buffer->device_pointer),
109  allocation->desc.offset,
110  offset,
111  allocation->mem->memory_size(),
112  0,
113  NULL,
114  NULL));
115  }
116 
117  allocation->desc.offset = offset;
118  offset += allocation->size;
119  }
120 
121  delete buffer;
122 
123  buffer = new_buffer;
124  }
125  else {
126  assert(total_size == buffer->data_size);
127 
128  size_t offset = 0;
129 
130  foreach (Allocation *allocation, allocations) {
131  if (allocation->needs_copy_to_device) {
132  /* Copy from host to device. */
133  opencl_device_assert(device,
134  clEnqueueWriteBuffer(device->cqCommandQueue,
135  CL_MEM_PTR(buffer->device_pointer),
136  CL_FALSE,
137  offset,
138  allocation->mem->memory_size(),
139  allocation->mem->host_pointer,
140  0,
141  NULL,
142  NULL));
143 
144  allocation->needs_copy_to_device = false;
145  }
146 
147  offset += allocation->size;
148  }
149  }
150 
151  /* Not really necessary, but seems to improve responsiveness for some reason. */
152  clFinish(device->cqCommandQueue);
153 }
154 
155 void MemoryManager::DeviceBuffer::free(OpenCLDevice *)
156 {
157  buffer->free();
158 }
159 
160 MemoryManager::DeviceBuffer *MemoryManager::smallest_device_buffer()
161 {
162  DeviceBuffer *smallest = device_buffers;
163 
164  foreach (DeviceBuffer &device_buffer, device_buffers) {
165  if (device_buffer.size < smallest->size) {
166  smallest = &device_buffer;
167  }
168  }
169 
170  return smallest;
171 }
172 
173 MemoryManager::MemoryManager(OpenCLDevice *device) : device(device), need_update(false)
174 {
175  foreach (DeviceBuffer &device_buffer, device_buffers) {
176  device_buffer.buffer = new device_only_memory<uchar>(device, "memory manager buffer");
177  }
178 }
179 
180 void MemoryManager::free()
181 {
182  foreach (DeviceBuffer &device_buffer, device_buffers) {
183  device_buffer.free(device);
184  }
185 }
186 
187 void MemoryManager::alloc(const char *name, device_memory &mem)
188 {
189  Allocation &allocation = allocations[name];
190 
191  allocation.mem = &mem;
192  allocation.needs_copy_to_device = true;
193 
194  if (!allocation.device_buffer) {
195  DeviceBuffer *device_buffer = smallest_device_buffer();
196  allocation.device_buffer = device_buffer;
197 
198  allocation.desc.device_buffer = device_buffer - device_buffers;
199 
200  device_buffer->add_allocation(allocation);
201 
202  device_buffer->size += mem.memory_size();
203  }
204 
205  need_update = true;
206 }
207 
209 {
210  foreach (AllocationsMap::value_type &value, allocations) {
211  Allocation &allocation = value.second;
212  if (allocation.mem == &mem) {
213 
214  allocation.device_buffer->size -= mem.memory_size();
215 
216  allocation.mem = NULL;
217  allocation.needs_copy_to_device = false;
218 
219  need_update = true;
220  return true;
221  }
222  }
223 
224  return false;
225 }
226 
228 {
230 
231  Allocation &allocation = allocations[name];
232  return allocation.desc;
233 }
234 
236 {
237  if (!need_update) {
238  return;
239  }
240 
241  need_update = false;
242 
243  foreach (DeviceBuffer &device_buffer, device_buffers) {
244  device_buffer.update_device_memory(device);
245  }
246 }
247 
248 void MemoryManager::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg)
249 {
251 
252  foreach (DeviceBuffer &device_buffer, device_buffers) {
253  if (device_buffer.buffer->device_pointer) {
254  device->kernel_set_args(kernel, (*narg)++, *device_buffer.buffer);
255  }
256  else {
257  device->kernel_set_args(kernel, (*narg)++);
258  }
259  }
260 }
261 
263 
264 #endif /* WITH_OPENCL */
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
void alloc(const char *name, device_memory &mem)
MemoryManager(OpenCLDevice *device)
BufferDescriptor get_descriptor(string name)
void set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg)
void update_device_memory()
size_t memory_size()
device_ptr device_pointer
void alloc_to_device(size_t num, bool shrink_to_fit=true)
#define CCL_NAMESPACE_END
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
float max
ccl_device_inline size_t align_up(size_t offset, size_t alignment)
Definition: util_types.h:65