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