1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "gpu/command_buffer/service/buffer_manager.h"
6#include <limits>
7#include "base/debug/trace_event.h"
8#include "base/logging.h"
9#include "gpu/command_buffer/common/gles2_cmd_utils.h"
10#include "gpu/command_buffer/service/context_state.h"
11#include "gpu/command_buffer/service/error_state.h"
12#include "gpu/command_buffer/service/feature_info.h"
13#include "gpu/command_buffer/service/memory_tracking.h"
14#include "ui/gl/gl_bindings.h"
15
16namespace gpu {
17namespace gles2 {
18
19BufferManager::BufferManager(
20    MemoryTracker* memory_tracker,
21    FeatureInfo* feature_info)
22    : memory_tracker_(
23          new MemoryTypeTracker(memory_tracker, MemoryTracker::kManaged)),
24      feature_info_(feature_info),
25      allow_buffers_on_multiple_targets_(false),
26      buffer_count_(0),
27      have_context_(true),
28      use_client_side_arrays_for_stream_buffers_(
29          feature_info ? feature_info->workarounds(
30              ).use_client_side_arrays_for_stream_buffers : 0) {
31}
32
33BufferManager::~BufferManager() {
34  DCHECK(buffers_.empty());
35  CHECK_EQ(buffer_count_, 0u);
36}
37
38void BufferManager::Destroy(bool have_context) {
39  have_context_ = have_context;
40  buffers_.clear();
41  DCHECK_EQ(0u, memory_tracker_->GetMemRepresented());
42}
43
44void BufferManager::CreateBuffer(GLuint client_id, GLuint service_id) {
45  scoped_refptr<Buffer> buffer(new Buffer(this, service_id));
46  std::pair<BufferMap::iterator, bool> result =
47      buffers_.insert(std::make_pair(client_id, buffer));
48  DCHECK(result.second);
49}
50
51Buffer* BufferManager::GetBuffer(
52    GLuint client_id) {
53  BufferMap::iterator it = buffers_.find(client_id);
54  return it != buffers_.end() ? it->second.get() : NULL;
55}
56
57void BufferManager::RemoveBuffer(GLuint client_id) {
58  BufferMap::iterator it = buffers_.find(client_id);
59  if (it != buffers_.end()) {
60    Buffer* buffer = it->second.get();
61    buffer->MarkAsDeleted();
62    buffers_.erase(it);
63  }
64}
65
66void BufferManager::StartTracking(Buffer* /* buffer */) {
67  ++buffer_count_;
68}
69
70void BufferManager::StopTracking(Buffer* buffer) {
71  memory_tracker_->TrackMemFree(buffer->size());
72  --buffer_count_;
73}
74
75Buffer::Buffer(BufferManager* manager, GLuint service_id)
76    : manager_(manager),
77      size_(0),
78      deleted_(false),
79      shadowed_(false),
80      is_client_side_array_(false),
81      service_id_(service_id),
82      target_(0),
83      usage_(GL_STATIC_DRAW) {
84  manager_->StartTracking(this);
85}
86
87Buffer::~Buffer() {
88  if (manager_) {
89    if (manager_->have_context_) {
90      GLuint id = service_id();
91      glDeleteBuffersARB(1, &id);
92    }
93    manager_->StopTracking(this);
94    manager_ = NULL;
95  }
96}
97
98void Buffer::SetInfo(
99    GLsizeiptr size, GLenum usage, bool shadow, const GLvoid* data,
100    bool is_client_side_array) {
101  usage_ = usage;
102  is_client_side_array_ = is_client_side_array;
103  ClearCache();
104  if (size != size_ || shadow != shadowed_) {
105    shadowed_ = shadow;
106    size_ = size;
107    if (shadowed_) {
108      shadow_.reset(new int8[size]);
109    } else {
110      shadow_.reset();
111    }
112  }
113  if (shadowed_) {
114    if (data) {
115      memcpy(shadow_.get(), data, size);
116    } else {
117      memset(shadow_.get(), 0, size);
118    }
119  }
120}
121
122bool Buffer::CheckRange(
123    GLintptr offset, GLsizeiptr size) const {
124  int32 end = 0;
125  return offset >= 0 && size >= 0 &&
126         offset <= std::numeric_limits<int32>::max() &&
127         size <= std::numeric_limits<int32>::max() &&
128         SafeAddInt32(offset, size, &end) && end <= size_;
129}
130
131bool Buffer::SetRange(
132    GLintptr offset, GLsizeiptr size, const GLvoid * data) {
133  if (!CheckRange(offset, size)) {
134    return false;
135  }
136  if (shadowed_) {
137    memcpy(shadow_.get() + offset, data, size);
138    ClearCache();
139  }
140  return true;
141}
142
143const void* Buffer::GetRange(
144    GLintptr offset, GLsizeiptr size) const {
145  if (!shadowed_) {
146    return NULL;
147  }
148  if (!CheckRange(offset, size)) {
149    return NULL;
150  }
151  return shadow_.get() + offset;
152}
153
154void Buffer::ClearCache() {
155  range_set_.clear();
156}
157
158template <typename T>
159GLuint GetMaxValue(const void* data, GLuint offset, GLsizei count) {
160  GLuint max_value = 0;
161  const T* element = reinterpret_cast<const T*>(
162      static_cast<const int8*>(data) + offset);
163  const T* end = element + count;
164  for (; element < end; ++element) {
165    if (*element > max_value) {
166      max_value = *element;
167    }
168  }
169  return max_value;
170}
171
172bool Buffer::GetMaxValueForRange(
173    GLuint offset, GLsizei count, GLenum type, GLuint* max_value) {
174  Range range(offset, count, type);
175  RangeToMaxValueMap::iterator it = range_set_.find(range);
176  if (it != range_set_.end()) {
177    *max_value = it->second;
178    return true;
179  }
180
181  uint32 size;
182  if (!SafeMultiplyUint32(
183      count, GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type), &size)) {
184    return false;
185  }
186
187  if (!SafeAddUint32(offset, size, &size)) {
188    return false;
189  }
190
191  if (size > static_cast<uint32>(size_)) {
192    return false;
193  }
194
195  if (!shadowed_) {
196    return false;
197  }
198
199  // Scan the range for the max value and store
200  GLuint max_v = 0;
201  switch (type) {
202    case GL_UNSIGNED_BYTE:
203      max_v = GetMaxValue<uint8>(shadow_.get(), offset, count);
204      break;
205    case GL_UNSIGNED_SHORT:
206      // Check we are not accessing an odd byte for a 2 byte value.
207      if ((offset & 1) != 0) {
208        return false;
209      }
210      max_v = GetMaxValue<uint16>(shadow_.get(), offset, count);
211      break;
212    case GL_UNSIGNED_INT:
213      // Check we are not accessing a non aligned address for a 4 byte value.
214      if ((offset & 3) != 0) {
215        return false;
216      }
217      max_v = GetMaxValue<uint32>(shadow_.get(), offset, count);
218      break;
219    default:
220      NOTREACHED();  // should never get here by validation.
221      break;
222  }
223  range_set_.insert(std::make_pair(range, max_v));
224  *max_value = max_v;
225  return true;
226}
227
228bool BufferManager::GetClientId(GLuint service_id, GLuint* client_id) const {
229  // This doesn't need to be fast. It's only used during slow queries.
230  for (BufferMap::const_iterator it = buffers_.begin();
231       it != buffers_.end(); ++it) {
232    if (it->second->service_id() == service_id) {
233      *client_id = it->first;
234      return true;
235    }
236  }
237  return false;
238}
239
240bool BufferManager::IsUsageClientSideArray(GLenum usage) {
241  return usage == GL_STREAM_DRAW && use_client_side_arrays_for_stream_buffers_;
242}
243
244bool BufferManager::UseNonZeroSizeForClientSideArrayBuffer() {
245  return feature_info_.get() &&
246         feature_info_->workarounds()
247             .use_non_zero_size_for_client_side_stream_buffers;
248}
249
250void BufferManager::SetInfo(
251    Buffer* buffer, GLsizeiptr size, GLenum usage, const GLvoid* data) {
252  DCHECK(buffer);
253  memory_tracker_->TrackMemFree(buffer->size());
254  bool is_client_side_array = IsUsageClientSideArray(usage);
255  bool shadow = buffer->target() == GL_ELEMENT_ARRAY_BUFFER ||
256                allow_buffers_on_multiple_targets_ ||
257                is_client_side_array;
258  buffer->SetInfo(size, usage, shadow, data, is_client_side_array);
259  memory_tracker_->TrackMemAlloc(buffer->size());
260}
261
262void BufferManager::ValidateAndDoBufferData(
263    ContextState* context_state, GLenum target, GLsizeiptr size,
264    const GLvoid * data, GLenum usage) {
265  ErrorState* error_state = context_state->GetErrorState();
266  if (!feature_info_->validators()->buffer_target.IsValid(target)) {
267    ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(
268        error_state, "glBufferData", target, "target");
269    return;
270  }
271  if (!feature_info_->validators()->buffer_usage.IsValid(usage)) {
272    ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(
273        error_state, "glBufferData", usage, "usage");
274    return;
275  }
276  if (size < 0) {
277    ERRORSTATE_SET_GL_ERROR(
278        error_state, GL_INVALID_VALUE, "glBufferData", "size < 0");
279    return;
280  }
281
282  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
283  if (!buffer) {
284    ERRORSTATE_SET_GL_ERROR(
285        error_state, GL_INVALID_VALUE, "glBufferData", "unknown buffer");
286    return;
287  }
288
289  if (!memory_tracker_->EnsureGPUMemoryAvailable(size)) {
290    ERRORSTATE_SET_GL_ERROR(
291        error_state, GL_OUT_OF_MEMORY, "glBufferData", "out of memory");
292    return;
293  }
294
295  DoBufferData(error_state, buffer, size, usage, data);
296}
297
298
299void BufferManager::DoBufferData(
300    ErrorState* error_state,
301    Buffer* buffer,
302    GLsizeiptr size,
303    GLenum usage,
304    const GLvoid* data) {
305  // Clear the buffer to 0 if no initial data was passed in.
306  scoped_ptr<int8[]> zero;
307  if (!data) {
308    zero.reset(new int8[size]);
309    memset(zero.get(), 0, size);
310    data = zero.get();
311  }
312
313  ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(error_state, "glBufferData");
314  if (IsUsageClientSideArray(usage)) {
315    GLsizei empty_size = UseNonZeroSizeForClientSideArrayBuffer() ? 1 : 0;
316    glBufferData(buffer->target(), empty_size, NULL, usage);
317  } else {
318    glBufferData(buffer->target(), size, data, usage);
319  }
320  GLenum error = ERRORSTATE_PEEK_GL_ERROR(error_state, "glBufferData");
321  if (error == GL_NO_ERROR) {
322    SetInfo(buffer, size, usage, data);
323  } else {
324    SetInfo(buffer, 0, usage, NULL);
325  }
326}
327
328void BufferManager::ValidateAndDoBufferSubData(
329  ContextState* context_state, GLenum target, GLintptr offset, GLsizeiptr size,
330  const GLvoid * data) {
331  ErrorState* error_state = context_state->GetErrorState();
332  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
333  if (!buffer) {
334    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, "glBufferSubData",
335                            "unknown buffer");
336    return;
337  }
338
339  DoBufferSubData(error_state, buffer, offset, size, data);
340}
341
342void BufferManager::DoBufferSubData(
343    ErrorState* error_state,
344    Buffer* buffer,
345    GLintptr offset,
346    GLsizeiptr size,
347    const GLvoid* data) {
348  if (!buffer->SetRange(offset, size, data)) {
349    ERRORSTATE_SET_GL_ERROR(
350        error_state, GL_INVALID_VALUE, "glBufferSubData", "out of range");
351    return;
352  }
353
354  if (!buffer->IsClientSideArray()) {
355    glBufferSubData(buffer->target(), offset, size, data);
356  }
357}
358
359void BufferManager::ValidateAndDoGetBufferParameteriv(
360    ContextState* context_state, GLenum target, GLenum pname, GLint* params) {
361  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
362  if (!buffer) {
363    ERRORSTATE_SET_GL_ERROR(
364        context_state->GetErrorState(), GL_INVALID_OPERATION,
365        "glGetBufferParameteriv", "no buffer bound for target");
366    return;
367  }
368  switch (pname) {
369    case GL_BUFFER_SIZE:
370      *params = buffer->size();
371      break;
372    case GL_BUFFER_USAGE:
373      *params = buffer->usage();
374      break;
375    default:
376      NOTREACHED();
377  }
378}
379
380bool BufferManager::SetTarget(Buffer* buffer, GLenum target) {
381  // Check that we are not trying to bind it to a different target.
382  if (buffer->target() != 0 && buffer->target() != target &&
383      !allow_buffers_on_multiple_targets_) {
384    return false;
385  }
386  if (buffer->target() == 0) {
387    buffer->set_target(target);
388  }
389  return true;
390}
391
392// Since one BufferManager can be shared by multiple decoders, ContextState is
393// passed in each time and not just passed in during initialization.
394Buffer* BufferManager::GetBufferInfoForTarget(
395    ContextState* state, GLenum target) {
396  DCHECK(target == GL_ARRAY_BUFFER || target == GL_ELEMENT_ARRAY_BUFFER);
397  if (target == GL_ARRAY_BUFFER) {
398    return state->bound_array_buffer.get();
399  } else {
400    return state->vertex_attrib_manager->element_array_buffer();
401  }
402}
403
404}  // namespace gles2
405}  // namespace gpu
406
407
408