6deed4e3f0d0f3335859242d300e434579aee5c4
[blender-staging.git] / intern / cycles / device / opencl / memory_manager.cpp
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
21 #include "device/opencl/opencl.h"
22 #include "device/opencl/memory_manager.h"
23
24 CCL_NAMESPACE_BEGIN
25
26 void MemoryManager::DeviceBuffer::add_allocation(Allocation& allocation)
27 {
28         allocations.push_back(&allocation);
29 }
30
31 void MemoryManager::DeviceBuffer::update_device_memory(OpenCLDeviceBase *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         if(need_realloc) {
68                 cl_ulong max_buffer_size;
69                 clGetDeviceInfo(device->cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_buffer_size, NULL);
70
71                 if(total_size > max_buffer_size) {
72                         device->set_error("Scene too complex to fit in available memory.");
73                         return;
74                 }
75
76                 device_memory *new_buffer = new device_memory(device,
77                                                               "memory manager buffer",
78                                                               MEM_READ_ONLY);
79
80                 new_buffer->resize(total_size);
81                 device->mem_alloc(*new_buffer);
82
83                 size_t offset = 0;
84
85                 foreach(Allocation* allocation, allocations) {
86                         if(allocation->needs_copy_to_device) {
87                                 /* Copy from host to device. */
88                                 opencl_device_assert(device, clEnqueueWriteBuffer(device->cqCommandQueue,
89                                         CL_MEM_PTR(new_buffer->device_pointer),
90                                         CL_FALSE,
91                                         offset,
92                                         allocation->mem->memory_size(),
93                                         (void*)allocation->mem->data_pointer,
94                                         0, NULL, NULL
95                                 ));
96
97                                 allocation->needs_copy_to_device = false;
98                         }
99                         else {
100                                 /* Fast copy from memory already on device. */
101                                 opencl_device_assert(device, clEnqueueCopyBuffer(device->cqCommandQueue,
102                                         CL_MEM_PTR(buffer->device_pointer),
103                                         CL_MEM_PTR(new_buffer->device_pointer),
104                                         allocation->desc.offset,
105                                         offset,
106                                         allocation->mem->memory_size(),
107                                         0, NULL, NULL
108                                 ));
109                         }
110
111                         allocation->desc.offset = offset;
112                         offset += allocation->size;
113                 }
114
115                 device->mem_free(*buffer);
116                 delete buffer;
117
118                 buffer = new_buffer;
119         }
120         else {
121                 assert(total_size == buffer->data_size);
122
123                 size_t offset = 0;
124
125                 foreach(Allocation* allocation, allocations) {
126                         if(allocation->needs_copy_to_device) {
127                                 /* Copy from host to device. */
128                                 opencl_device_assert(device, clEnqueueWriteBuffer(device->cqCommandQueue,
129                                         CL_MEM_PTR(buffer->device_pointer),
130                                         CL_FALSE,
131                                         offset,
132                                         allocation->mem->memory_size(),
133                                         (void*)allocation->mem->data_pointer,
134                                         0, NULL, NULL
135                                 ));
136
137                                 allocation->needs_copy_to_device = false;
138                         }
139
140                         offset += allocation->size;
141                 }
142         }
143
144         /* Not really necessary, but seems to improve responsiveness for some reason. */
145         clFinish(device->cqCommandQueue);
146 }
147
148 void MemoryManager::DeviceBuffer::free(OpenCLDeviceBase *device)
149 {
150         device->mem_free(*buffer);
151 }
152
153 MemoryManager::DeviceBuffer* MemoryManager::smallest_device_buffer()
154 {
155         DeviceBuffer* smallest = device_buffers;
156
157         foreach(DeviceBuffer& device_buffer, device_buffers) {
158                 if(device_buffer.size < smallest->size) {
159                         smallest = &device_buffer;
160                 }
161         }
162
163         return smallest;
164 }
165
166 MemoryManager::MemoryManager(OpenCLDeviceBase *device)
167 : device(device), need_update(false)
168 {
169         foreach(DeviceBuffer& device_buffer, device_buffers) {
170                 device_buffer.buffer = new device_memory(device,
171                                                          "memory manager buffer",
172                                                          MEM_READ_ONLY);
173         }
174 }
175
176 void MemoryManager::free()
177 {
178         foreach(DeviceBuffer& device_buffer, device_buffers) {
179                 device_buffer.free(device);
180         }
181 }
182
183 void MemoryManager::alloc(const char *name, device_memory& mem)
184 {
185         Allocation& allocation = allocations[name];
186
187         allocation.mem = &mem;
188         allocation.needs_copy_to_device = true;
189
190         if(!allocation.device_buffer) {
191                 DeviceBuffer* device_buffer = smallest_device_buffer();
192                 allocation.device_buffer = device_buffer;
193
194                 allocation.desc.device_buffer = device_buffer - device_buffers;
195
196                 device_buffer->add_allocation(allocation);
197
198                 device_buffer->size += mem.memory_size();
199         }
200
201         need_update = true;
202 }
203
204 bool MemoryManager::free(device_memory& mem)
205 {
206         foreach(AllocationsMap::value_type& value, allocations) {
207                 Allocation& allocation = value.second;
208                 if(allocation.mem == &mem) {
209
210                         allocation.device_buffer->size -= mem.memory_size();
211
212                         allocation.mem = NULL;
213                         allocation.needs_copy_to_device = false;
214
215                         need_update = true;
216                         return true;
217                 }
218         }
219
220         return false;
221 }
222
223 MemoryManager::BufferDescriptor MemoryManager::get_descriptor(string name)
224 {
225         update_device_memory();
226
227         Allocation& allocation = allocations[name];
228         return allocation.desc;
229 }
230
231 void MemoryManager::update_device_memory()
232 {
233         if(!need_update) {
234                 return;
235         }
236
237         need_update = false;
238
239         foreach(DeviceBuffer& device_buffer, device_buffers) {
240                 device_buffer.update_device_memory(device);
241         }
242 }
243
244 void MemoryManager::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg)
245 {
246         update_device_memory();
247
248         foreach(DeviceBuffer& device_buffer, device_buffers) {
249                 if(device_buffer.buffer->device_pointer) {
250                         device->kernel_set_args(kernel, (*narg)++, *device_buffer.buffer);
251                 }
252                 else {
253                         device->kernel_set_args(kernel, (*narg)++, device->null_mem);
254                 }
255         }
256 }
257
258 CCL_NAMESPACE_END
259
260 #endif  /* WITH_OPENCL */
261