buffer_manager.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/error_state.h"
11#include "gpu/command_buffer/service/feature_info.h"
12#include "gpu/command_buffer/service/memory_tracking.h"
13#include "ui/gl/gl_bindings.h"
14
15namespace gpu {
16namespace gles2 {
17
18BufferManager::BufferManager(
19    MemoryTracker* memory_tracker,
20    FeatureInfo* feature_info)
21    : memory_tracker_(
22          new MemoryTypeTracker(memory_tracker, MemoryTracker::kManaged)),
23      feature_info_(feature_info),
24      allow_buffers_on_multiple_targets_(false),
25      buffer_count_(0),
26      have_context_(true),
27      use_client_side_arrays_for_stream_buffers_(
28          feature_info ? feature_info->workarounds(
29              ).use_client_side_arrays_for_stream_buffers : 0) {
30}
31
32BufferManager::~BufferManager() {
33  DCHECK(buffers_.empty());
34  CHECK_EQ(buffer_count_, 0u);
35}
36
37void BufferManager::Destroy(bool have_context) {
38  have_context_ = have_context;
39  buffers_.clear();
40  DCHECK_EQ(0u, memory_tracker_->GetMemRepresented());
41}
42
43void BufferManager::CreateBuffer(GLuint client_id, GLuint service_id) {
44  scoped_refptr<Buffer> buffer(new Buffer(this, service_id));
45  std::pair<BufferMap::iterator, bool> result =
46      buffers_.insert(std::make_pair(client_id, buffer));
47  DCHECK(result.second);
48}
49
50Buffer* BufferManager::GetBuffer(
51    GLuint client_id) {
52  BufferMap::iterator it = buffers_.find(client_id);
53  return it != buffers_.end() ? it->second : NULL;
54}
55
56void BufferManager::RemoveBuffer(GLuint client_id) {
57  BufferMap::iterator it = buffers_.find(client_id);
58  if (it != buffers_.end()) {
59    Buffer* buffer = it->second.get();
60    buffer->MarkAsDeleted();
61    buffers_.erase(it);
62  }
63}
64
65void BufferManager::StartTracking(Buffer* /* buffer */) {
66  ++buffer_count_;
67}
68
69void BufferManager::StopTracking(Buffer* buffer) {
70  memory_tracker_->TrackMemFree(buffer->size());
71  --buffer_count_;
72}
73
74Buffer::Buffer(BufferManager* manager, GLuint service_id)
75    : manager_(manager),
76      deleted_(false),
77      service_id_(service_id),
78      target_(0),
79      size_(0),
80      usage_(GL_STATIC_DRAW),
81      shadowed_(false),
82      is_client_side_array_(false) {
83  manager_->StartTracking(this);
84}
85
86Buffer::~Buffer() {
87  if (manager_) {
88    if (manager_->have_context_) {
89      GLuint id = service_id();
90      glDeleteBuffersARB(1, &id);
91    }
92    manager_->StopTracking(this);
93    manager_ = NULL;
94  }
95}
96
97void Buffer::SetInfo(
98    GLsizeiptr size, GLenum usage, bool shadow, const GLvoid* data,
99    bool is_client_side_array) {
100  usage_ = usage;
101  is_client_side_array_ = is_client_side_array;
102  ClearCache();
103  if (size != size_ || shadow != shadowed_) {
104    shadowed_ = shadow;
105    size_ = size;
106    if (shadowed_) {
107      shadow_.reset(new int8[size]);
108    } else {
109      shadow_.reset();
110    }
111  }
112  if (shadowed_) {
113    if (data) {
114      memcpy(shadow_.get(), data, size);
115    } else {
116      memset(shadow_.get(), 0, size);
117    }
118  }
119}
120
121bool Buffer::CheckRange(
122    GLintptr offset, GLsizeiptr size) const {
123  int32 end = 0;
124  return offset >= 0 && size >= 0 &&
125         offset <= std::numeric_limits<int32>::max() &&
126         size <= std::numeric_limits<int32>::max() &&
127         SafeAddInt32(offset, size, &end) && end <= size_;
128}
129
130bool Buffer::SetRange(
131    GLintptr offset, GLsizeiptr size, const GLvoid * data) {
132  if (!CheckRange(offset, size)) {
133    return false;
134  }
135  if (shadowed_) {
136    memcpy(shadow_.get() + offset, data, size);
137    ClearCache();
138  }
139  return true;
140}
141
142const void* Buffer::GetRange(
143    GLintptr offset, GLsizeiptr size) const {
144  if (!shadowed_) {
145    return NULL;
146  }
147  if (!CheckRange(offset, size)) {
148    return NULL;
149  }
150  return shadow_.get() + offset;
151}
152
153void Buffer::ClearCache() {
154  range_set_.clear();
155}
156
157template <typename T>
158GLuint GetMaxValue(const void* data, GLuint offset, GLsizei count) {
159  GLuint max_value = 0;
160  const T* element = reinterpret_cast<const T*>(
161      static_cast<const int8*>(data) + offset);
162  const T* end = element + count;
163  for (; element < end; ++element) {
164    if (*element > max_value) {
165      max_value = *element;
166    }
167  }
168  return max_value;
169}
170
171bool Buffer::GetMaxValueForRange(
172    GLuint offset, GLsizei count, GLenum type, GLuint* max_value) {
173  Range range(offset, count, type);
174  RangeToMaxValueMap::iterator it = range_set_.find(range);
175  if (it != range_set_.end()) {
176    *max_value = it->second;
177    return true;
178  }
179
180  uint32 size;
181  if (!SafeMultiplyUint32(
182      count, GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type), &size)) {
183    return false;
184  }
185
186  if (!SafeAddUint32(offset, size, &size)) {
187    return false;
188  }
189
190  if (size > static_cast<uint32>(size_)) {
191    return false;
192  }
193
194  if (!shadowed_) {
195    return false;
196  }
197
198  // Scan the range for the max value and store
199  GLuint max_v = 0;
200  switch (type) {
201    case GL_UNSIGNED_BYTE:
202      max_v = GetMaxValue<uint8>(shadow_.get(), offset, count);
203      break;
204    case GL_UNSIGNED_SHORT:
205      // Check we are not accessing an odd byte for a 2 byte value.
206      if ((offset & 1) != 0) {
207        return false;
208      }
209      max_v = GetMaxValue<uint16>(shadow_.get(), offset, count);
210      break;
211    case GL_UNSIGNED_INT:
212      // Check we are not accessing a non aligned address for a 4 byte value.
213      if ((offset & 3) != 0) {
214        return false;
215      }
216      max_v = GetMaxValue<uint32>(shadow_.get(), offset, count);
217      break;
218    default:
219      NOTREACHED();  // should never get here by validation.
220      break;
221  }
222  range_set_.insert(std::make_pair(range, max_v));
223  *max_value = max_v;
224  return true;
225}
226
227bool BufferManager::GetClientId(GLuint service_id, GLuint* client_id) const {
228  // This doesn't need to be fast. It's only used during slow queries.
229  for (BufferMap::const_iterator it = buffers_.begin();
230       it != buffers_.end(); ++it) {
231    if (it->second->service_id() == service_id) {
232      *client_id = it->first;
233      return true;
234    }
235  }
236  return false;
237}
238
239bool BufferManager::IsUsageClientSideArray(GLenum usage) {
240  return usage == GL_STREAM_DRAW && use_client_side_arrays_for_stream_buffers_;
241}
242
243bool BufferManager::UseNonZeroSizeForClientSideArrayBuffer() {
244  return feature_info_ && feature_info_->workarounds(
245              ).use_non_zero_size_for_client_side_stream_buffers;
246}
247
248void BufferManager::SetInfo(
249    Buffer* buffer, GLsizeiptr size, GLenum usage, const GLvoid* data) {
250  DCHECK(buffer);
251  memory_tracker_->TrackMemFree(buffer->size());
252  bool is_client_side_array = IsUsageClientSideArray(usage);
253  bool shadow = buffer->target() == GL_ELEMENT_ARRAY_BUFFER ||
254                allow_buffers_on_multiple_targets_ ||
255                is_client_side_array;
256  buffer->SetInfo(size, usage, shadow, data, is_client_side_array);
257  memory_tracker_->TrackMemAlloc(buffer->size());
258}
259
260void BufferManager::DoBufferData(
261    ErrorState* error_state,
262    Buffer* buffer,
263    GLsizeiptr size,
264    GLenum usage,
265    const GLvoid* data) {
266  // Clear the buffer to 0 if no initial data was passed in.
267  scoped_ptr<int8[]> zero;
268  if (!data) {
269    zero.reset(new int8[size]);
270    memset(zero.get(), 0, size);
271    data = zero.get();
272  }
273
274  ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(error_state, "glBufferData");
275  if (IsUsageClientSideArray(usage)) {
276    GLsizei empty_size = UseNonZeroSizeForClientSideArrayBuffer() ? 1 : 0;
277    glBufferData(buffer->target(), empty_size, NULL, usage);
278  } else {
279    glBufferData(buffer->target(), size, data, usage);
280  }
281  GLenum error = ERRORSTATE_PEEK_GL_ERROR(error_state, "glBufferData");
282  if (error == GL_NO_ERROR) {
283    SetInfo(buffer, size, usage, data);
284  } else {
285    SetInfo(buffer, 0, usage, NULL);
286  }
287}
288
289void BufferManager::DoBufferSubData(
290    ErrorState* error_state,
291    Buffer* buffer,
292    GLintptr offset,
293    GLsizeiptr size,
294    const GLvoid* data) {
295  if (!buffer->SetRange(offset, size, data)) {
296    ERRORSTATE_SET_GL_ERROR(
297        error_state, GL_INVALID_VALUE, "glBufferSubData", "out of range");
298    return;
299  }
300
301  if (!buffer->IsClientSideArray()) {
302    glBufferSubData(buffer->target(), offset, size, data);
303  }
304}
305
306bool BufferManager::SetTarget(Buffer* buffer, GLenum target) {
307  // Check that we are not trying to bind it to a different target.
308  if (buffer->target() != 0 && buffer->target() != target &&
309      !allow_buffers_on_multiple_targets_) {
310    return false;
311  }
312  if (buffer->target() == 0) {
313    buffer->set_target(target);
314  }
315  return true;
316}
317
318}  // namespace gles2
319}  // namespace gpu
320
321
322