1 //
2 // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // VertexDataManager.h: Defines the VertexDataManager, a class that
8 // runs the Buffer translation process.
9 
10 #include "libANGLE/renderer/d3d/VertexDataManager.h"
11 
12 #include "common/bitset_utils.h"
13 #include "libANGLE/Buffer.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/Program.h"
16 #include "libANGLE/State.h"
17 #include "libANGLE/VertexArray.h"
18 #include "libANGLE/VertexAttribute.h"
19 #include "libANGLE/formatutils.h"
20 #include "libANGLE/renderer/d3d/BufferD3D.h"
21 #include "libANGLE/renderer/d3d/RendererD3D.h"
22 #include "libANGLE/renderer/d3d/VertexBuffer.h"
23 
24 using namespace angle;
25 
26 namespace rx
27 {
28 namespace
29 {
30 enum
31 {
32     INITIAL_STREAM_BUFFER_SIZE = 1024 * 1024
33 };
34 // This has to be at least 4k or else it fails on ATI cards.
35 enum
36 {
37     CONSTANT_VERTEX_BUFFER_SIZE = 4096
38 };
39 
40 // Warning: you should ensure binding really matches attrib.bindingIndex before using this function.
ElementsInBuffer(const gl::VertexAttribute & attrib,const gl::VertexBinding & binding,unsigned int size)41 int ElementsInBuffer(const gl::VertexAttribute &attrib,
42                      const gl::VertexBinding &binding,
43                      unsigned int size)
44 {
45     // Size cannot be larger than a GLsizei
46     if (size > static_cast<unsigned int>(std::numeric_limits<int>::max()))
47     {
48         size = static_cast<unsigned int>(std::numeric_limits<int>::max());
49     }
50 
51     GLsizei stride = static_cast<GLsizei>(ComputeVertexAttributeStride(attrib, binding));
52     GLsizei offset = static_cast<GLsizei>(ComputeVertexAttributeOffset(attrib, binding));
53     return (size - offset % stride +
54             (stride - static_cast<GLsizei>(ComputeVertexAttributeTypeSize(attrib)))) /
55            stride;
56 }
57 
58 // Warning: you should ensure binding really matches attrib.bindingIndex before using this function.
DirectStoragePossible(const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)59 bool DirectStoragePossible(const gl::VertexAttribute &attrib, const gl::VertexBinding &binding)
60 {
61     // Current value attribs may not use direct storage.
62     if (!attrib.enabled)
63     {
64         return false;
65     }
66 
67     gl::Buffer *buffer = binding.getBuffer().get();
68     if (!buffer)
69     {
70         return false;
71     }
72 
73     BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
74     ASSERT(bufferD3D);
75     if (!bufferD3D->supportsDirectBinding())
76     {
77         return false;
78     }
79 
80     // Alignment restrictions: In D3D, vertex data must be aligned to the format stride, or to a
81     // 4-byte boundary, whichever is smaller. (Undocumented, and experimentally confirmed)
82     size_t alignment = 4;
83 
84     // TODO(jmadill): add VertexFormatCaps
85     BufferFactoryD3D *factory = bufferD3D->getFactory();
86 
87     gl::VertexFormatType vertexFormatType = gl::GetVertexFormatType(attrib);
88 
89     // CPU-converted vertex data must be converted (naturally).
90     if ((factory->getVertexConversionType(vertexFormatType) & VERTEX_CONVERT_CPU) != 0)
91     {
92         return false;
93     }
94 
95     if (attrib.type != GL_FLOAT)
96     {
97         auto errorOrElementSize = factory->getVertexSpaceRequired(attrib, binding, 1, 0);
98         if (errorOrElementSize.isError())
99         {
100             ERR() << "Unlogged error in DirectStoragePossible.";
101             return false;
102         }
103 
104         alignment = std::min<size_t>(errorOrElementSize.getResult(), 4);
105     }
106 
107     GLintptr offset = ComputeVertexAttributeOffset(attrib, binding);
108     // Final alignment check - unaligned data must be converted.
109     return (static_cast<size_t>(ComputeVertexAttributeStride(attrib, binding)) % alignment == 0) &&
110            (static_cast<size_t>(offset) % alignment == 0);
111 }
112 }  // anonymous namespace
113 
TranslatedAttribute()114 TranslatedAttribute::TranslatedAttribute()
115     : active(false),
116       attribute(nullptr),
117       binding(nullptr),
118       currentValueType(GL_NONE),
119       baseOffset(0),
120       usesFirstVertexOffset(false),
121       stride(0),
122       vertexBuffer(),
123       storage(nullptr),
124       serial(0),
125       divisor(0)
126 {
127 }
128 
129 TranslatedAttribute::TranslatedAttribute(const TranslatedAttribute &other) = default;
130 
computeOffset(GLint startVertex) const131 gl::ErrorOrResult<unsigned int> TranslatedAttribute::computeOffset(GLint startVertex) const
132 {
133     if (!usesFirstVertexOffset)
134     {
135         return baseOffset;
136     }
137 
138     CheckedNumeric<unsigned int> offset;
139 
140     offset = baseOffset + stride * static_cast<unsigned int>(startVertex);
141     ANGLE_TRY_CHECKED_MATH(offset);
142     return offset.ValueOrDie();
143 }
144 
145 // Warning: you should ensure binding really matches attrib.bindingIndex before using this function.
ClassifyAttributeStorage(const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)146 VertexStorageType ClassifyAttributeStorage(const gl::VertexAttribute &attrib,
147                                            const gl::VertexBinding &binding)
148 {
149     // If attribute is disabled, we use the current value.
150     if (!attrib.enabled)
151     {
152         return VertexStorageType::CURRENT_VALUE;
153     }
154 
155     // If specified with immediate data, we must use dynamic storage.
156     auto *buffer = binding.getBuffer().get();
157     if (!buffer)
158     {
159         return VertexStorageType::DYNAMIC;
160     }
161 
162     // Check if the buffer supports direct storage.
163     if (DirectStoragePossible(attrib, binding))
164     {
165         return VertexStorageType::DIRECT;
166     }
167 
168     // Otherwise the storage is static or dynamic.
169     BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
170     ASSERT(bufferD3D);
171     switch (bufferD3D->getUsage())
172     {
173         case D3DBufferUsage::DYNAMIC:
174             return VertexStorageType::DYNAMIC;
175         case D3DBufferUsage::STATIC:
176             return VertexStorageType::STATIC;
177         default:
178             UNREACHABLE();
179             return VertexStorageType::UNKNOWN;
180     }
181 }
182 
CurrentValueState()183 VertexDataManager::CurrentValueState::CurrentValueState() : buffer(), offset(0)
184 {
185     data.FloatValues[0] = std::numeric_limits<float>::quiet_NaN();
186     data.FloatValues[1] = std::numeric_limits<float>::quiet_NaN();
187     data.FloatValues[2] = std::numeric_limits<float>::quiet_NaN();
188     data.FloatValues[3] = std::numeric_limits<float>::quiet_NaN();
189     data.Type = GL_FLOAT;
190 }
191 
~CurrentValueState()192 VertexDataManager::CurrentValueState::~CurrentValueState()
193 {
194 }
195 
VertexDataManager(BufferFactoryD3D * factory)196 VertexDataManager::VertexDataManager(BufferFactoryD3D *factory)
197     : mFactory(factory), mStreamingBuffer(), mCurrentValueCache(gl::MAX_VERTEX_ATTRIBS)
198 {
199 }
200 
~VertexDataManager()201 VertexDataManager::~VertexDataManager()
202 {
203 }
204 
initialize()205 gl::Error VertexDataManager::initialize()
206 {
207     mStreamingBuffer.reset(
208         new StreamingVertexBufferInterface(mFactory, INITIAL_STREAM_BUFFER_SIZE));
209     if (!mStreamingBuffer)
210     {
211         return gl::OutOfMemory() << "Failed to allocate the streaming vertex buffer.";
212     }
213 
214     return gl::NoError();
215 }
216 
deinitialize()217 void VertexDataManager::deinitialize()
218 {
219     mStreamingBuffer.reset();
220     mCurrentValueCache.clear();
221 }
222 
prepareVertexData(const gl::Context * context,GLint start,GLsizei count,std::vector<TranslatedAttribute> * translatedAttribs,GLsizei instances)223 gl::Error VertexDataManager::prepareVertexData(const gl::Context *context,
224                                                GLint start,
225                                                GLsizei count,
226                                                std::vector<TranslatedAttribute> *translatedAttribs,
227                                                GLsizei instances)
228 {
229     ASSERT(mStreamingBuffer);
230 
231     const gl::State &state             = context->getGLState();
232     const gl::VertexArray *vertexArray = state.getVertexArray();
233     const auto &vertexAttributes       = vertexArray->getVertexAttributes();
234     const auto &vertexBindings         = vertexArray->getVertexBindings();
235 
236     mDynamicAttribsMaskCache.reset();
237     const gl::Program *program = state.getProgram();
238 
239     translatedAttribs->clear();
240 
241     for (size_t attribIndex = 0; attribIndex < vertexAttributes.size(); ++attribIndex)
242     {
243         // Skip attrib locations the program doesn't use.
244         if (!program->isAttribLocationActive(attribIndex))
245             continue;
246 
247         const auto &attrib = vertexAttributes[attribIndex];
248         const auto &binding = vertexBindings[attrib.bindingIndex];
249 
250         // Resize automatically puts in empty attribs
251         translatedAttribs->resize(attribIndex + 1);
252 
253         TranslatedAttribute *translated = &(*translatedAttribs)[attribIndex];
254         auto currentValueData           = state.getVertexAttribCurrentValue(attribIndex);
255 
256         // Record the attribute now
257         translated->active           = true;
258         translated->attribute        = &attrib;
259         translated->binding          = &binding;
260         translated->currentValueType = currentValueData.Type;
261         translated->divisor          = binding.getDivisor();
262 
263         switch (ClassifyAttributeStorage(attrib, binding))
264         {
265             case VertexStorageType::STATIC:
266             {
267                 // Store static attribute.
268                 ANGLE_TRY(StoreStaticAttrib(context, translated));
269                 break;
270             }
271             case VertexStorageType::DYNAMIC:
272                 // Dynamic attributes must be handled together.
273                 mDynamicAttribsMaskCache.set(attribIndex);
274                 break;
275             case VertexStorageType::DIRECT:
276                 // Update translated data for direct attributes.
277                 StoreDirectAttrib(translated);
278                 break;
279             case VertexStorageType::CURRENT_VALUE:
280             {
281                 ANGLE_TRY(storeCurrentValue(currentValueData, translated, attribIndex));
282                 break;
283             }
284             default:
285                 UNREACHABLE();
286                 break;
287         }
288     }
289 
290     if (mDynamicAttribsMaskCache.none())
291     {
292         return gl::NoError();
293     }
294 
295     ANGLE_TRY(storeDynamicAttribs(context, translatedAttribs, mDynamicAttribsMaskCache, start,
296                                   count, instances));
297 
298     PromoteDynamicAttribs(context, *translatedAttribs, mDynamicAttribsMaskCache, count);
299 
300     return gl::NoError();
301 }
302 
303 // static
StoreDirectAttrib(TranslatedAttribute * directAttrib)304 void VertexDataManager::StoreDirectAttrib(TranslatedAttribute *directAttrib)
305 {
306     ASSERT(directAttrib->attribute && directAttrib->binding);
307     const auto &attrib  = *directAttrib->attribute;
308     const auto &binding = *directAttrib->binding;
309 
310     gl::Buffer *buffer   = binding.getBuffer().get();
311     ASSERT(buffer);
312     BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
313 
314     ASSERT(DirectStoragePossible(attrib, binding));
315     directAttrib->vertexBuffer.set(nullptr);
316     directAttrib->storage = bufferD3D;
317     directAttrib->serial  = bufferD3D->getSerial();
318     directAttrib->stride = static_cast<unsigned int>(ComputeVertexAttributeStride(attrib, binding));
319     directAttrib->baseOffset =
320         static_cast<unsigned int>(ComputeVertexAttributeOffset(attrib, binding));
321 
322     // Instanced vertices do not apply the 'start' offset
323     directAttrib->usesFirstVertexOffset = (binding.getDivisor() == 0);
324 }
325 
326 // static
StoreStaticAttrib(const gl::Context * context,TranslatedAttribute * translated)327 gl::Error VertexDataManager::StoreStaticAttrib(const gl::Context *context,
328                                                TranslatedAttribute *translated)
329 {
330     ASSERT(translated->attribute && translated->binding);
331     const auto &attrib  = *translated->attribute;
332     const auto &binding = *translated->binding;
333 
334     gl::Buffer *buffer = binding.getBuffer().get();
335     ASSERT(buffer && attrib.enabled && !DirectStoragePossible(attrib, binding));
336     BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
337 
338     // Compute source data pointer
339     const uint8_t *sourceData = nullptr;
340     const int offset          = static_cast<int>(ComputeVertexAttributeOffset(attrib, binding));
341 
342     ANGLE_TRY(bufferD3D->getData(context, &sourceData));
343     sourceData += offset;
344 
345     unsigned int streamOffset = 0;
346 
347     translated->storage = nullptr;
348     ANGLE_TRY_RESULT(bufferD3D->getFactory()->getVertexSpaceRequired(attrib, binding, 1, 0),
349                      translated->stride);
350 
351     auto *staticBuffer = bufferD3D->getStaticVertexBuffer(attrib, binding);
352     ASSERT(staticBuffer);
353 
354     if (staticBuffer->empty())
355     {
356         // Convert the entire buffer
357         int totalCount =
358             ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize()));
359         int startIndex = offset / static_cast<int>(ComputeVertexAttributeStride(attrib, binding));
360 
361         ANGLE_TRY(staticBuffer->storeStaticAttribute(attrib, binding, -startIndex, totalCount, 0,
362                                                      sourceData));
363     }
364 
365     unsigned int firstElementOffset =
366         (static_cast<unsigned int>(offset) /
367          static_cast<unsigned int>(ComputeVertexAttributeStride(attrib, binding))) *
368         translated->stride;
369 
370     VertexBuffer *vertexBuffer = staticBuffer->getVertexBuffer();
371 
372     CheckedNumeric<unsigned int> checkedOffset(streamOffset);
373     checkedOffset += firstElementOffset;
374 
375     if (!checkedOffset.IsValid())
376     {
377         return gl::InternalError() << "Integer overflow in VertexDataManager::StoreStaticAttrib";
378     }
379 
380     translated->vertexBuffer.set(vertexBuffer);
381     translated->serial = vertexBuffer->getSerial();
382     translated->baseOffset = streamOffset + firstElementOffset;
383 
384     // Instanced vertices do not apply the 'start' offset
385     translated->usesFirstVertexOffset = (binding.getDivisor() == 0);
386 
387     return gl::NoError();
388 }
389 
storeDynamicAttribs(const gl::Context * context,std::vector<TranslatedAttribute> * translatedAttribs,const gl::AttributesMask & dynamicAttribsMask,GLint start,GLsizei count,GLsizei instances)390 gl::Error VertexDataManager::storeDynamicAttribs(
391     const gl::Context *context,
392     std::vector<TranslatedAttribute> *translatedAttribs,
393     const gl::AttributesMask &dynamicAttribsMask,
394     GLint start,
395     GLsizei count,
396     GLsizei instances)
397 {
398     // Instantiating this class will ensure the streaming buffer is never left mapped.
399     class StreamingBufferUnmapper final : NonCopyable
400     {
401       public:
402         StreamingBufferUnmapper(StreamingVertexBufferInterface *streamingBuffer)
403             : mStreamingBuffer(streamingBuffer)
404         {
405             ASSERT(mStreamingBuffer);
406         }
407         ~StreamingBufferUnmapper() { mStreamingBuffer->getVertexBuffer()->hintUnmapResource(); }
408 
409       private:
410         StreamingVertexBufferInterface *mStreamingBuffer;
411     };
412 
413     // Will trigger unmapping on return.
414     StreamingBufferUnmapper localUnmapper(mStreamingBuffer.get());
415 
416     // Reserve the required space for the dynamic buffers.
417     for (auto attribIndex : dynamicAttribsMask)
418     {
419         const auto &dynamicAttrib = (*translatedAttribs)[attribIndex];
420         ANGLE_TRY(reserveSpaceForAttrib(dynamicAttrib, start, count, instances));
421     }
422 
423     // Store dynamic attributes
424     for (auto attribIndex : dynamicAttribsMask)
425     {
426         auto *dynamicAttrib = &(*translatedAttribs)[attribIndex];
427         ANGLE_TRY(storeDynamicAttrib(context, dynamicAttrib, start, count, instances));
428     }
429 
430     return gl::NoError();
431 }
432 
PromoteDynamicAttribs(const gl::Context * context,const std::vector<TranslatedAttribute> & translatedAttribs,const gl::AttributesMask & dynamicAttribsMask,GLsizei count)433 void VertexDataManager::PromoteDynamicAttribs(
434     const gl::Context *context,
435     const std::vector<TranslatedAttribute> &translatedAttribs,
436     const gl::AttributesMask &dynamicAttribsMask,
437     GLsizei count)
438 {
439     for (auto attribIndex : dynamicAttribsMask)
440     {
441         const auto &dynamicAttrib = translatedAttribs[attribIndex];
442         ASSERT(dynamicAttrib.attribute && dynamicAttrib.binding);
443         const auto &binding       = *dynamicAttrib.binding;
444 
445         gl::Buffer *buffer = binding.getBuffer().get();
446         if (buffer)
447         {
448             BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
449             size_t typeSize      = ComputeVertexAttributeTypeSize(*dynamicAttrib.attribute);
450             bufferD3D->promoteStaticUsage(context, count * static_cast<int>(typeSize));
451         }
452     }
453 }
454 
reserveSpaceForAttrib(const TranslatedAttribute & translatedAttrib,GLint start,GLsizei count,GLsizei instances) const455 gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &translatedAttrib,
456                                                    GLint start,
457                                                    GLsizei count,
458                                                    GLsizei instances) const
459 {
460     ASSERT(translatedAttrib.attribute && translatedAttrib.binding);
461     const auto &attrib  = *translatedAttrib.attribute;
462     const auto &binding = *translatedAttrib.binding;
463 
464     ASSERT(!DirectStoragePossible(attrib, binding));
465 
466     gl::Buffer *buffer   = binding.getBuffer().get();
467     BufferD3D *bufferD3D = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
468     ASSERT(!bufferD3D || bufferD3D->getStaticVertexBuffer(attrib, binding) == nullptr);
469 
470     size_t totalCount = gl::ComputeVertexBindingElementCount(
471         binding.getDivisor(), static_cast<size_t>(count), static_cast<size_t>(instances));
472     // TODO(jiajia.qin@intel.com): force the index buffer to clamp any out of range indices instead
473     // of invalid operation here.
474     if (bufferD3D)
475     {
476         // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
477         // a non-instanced draw call
478         GLint firstVertexIndex = binding.getDivisor() > 0 ? 0 : start;
479         int64_t maxVertexCount =
480             static_cast<int64_t>(firstVertexIndex) + static_cast<int64_t>(totalCount);
481         int elementsInBuffer =
482             ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize()));
483 
484         if (maxVertexCount > elementsInBuffer)
485         {
486             return gl::InvalidOperation() << "Vertex buffer is not big enough for the draw call.";
487         }
488     }
489     return mStreamingBuffer->reserveVertexSpace(attrib, binding, static_cast<GLsizei>(totalCount),
490                                                 instances);
491 }
492 
storeDynamicAttrib(const gl::Context * context,TranslatedAttribute * translated,GLint start,GLsizei count,GLsizei instances)493 gl::Error VertexDataManager::storeDynamicAttrib(const gl::Context *context,
494                                                 TranslatedAttribute *translated,
495                                                 GLint start,
496                                                 GLsizei count,
497                                                 GLsizei instances)
498 {
499     ASSERT(translated->attribute && translated->binding);
500     const auto &attrib  = *translated->attribute;
501     const auto &binding = *translated->binding;
502 
503     gl::Buffer *buffer = binding.getBuffer().get();
504     ASSERT(buffer || attrib.pointer);
505     ASSERT(attrib.enabled);
506 
507     BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
508 
509     // Instanced vertices do not apply the 'start' offset
510     GLint firstVertexIndex = (binding.getDivisor() > 0 ? 0 : start);
511 
512     // Compute source data pointer
513     const uint8_t *sourceData = nullptr;
514 
515     if (buffer)
516     {
517         ANGLE_TRY(storage->getData(context, &sourceData));
518         sourceData += static_cast<int>(ComputeVertexAttributeOffset(attrib, binding));
519     }
520     else
521     {
522         // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
523         // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
524         sourceData = static_cast<const uint8_t*>(attrib.pointer);
525     }
526 
527     unsigned int streamOffset = 0;
528 
529     translated->storage = nullptr;
530     ANGLE_TRY_RESULT(mFactory->getVertexSpaceRequired(attrib, binding, 1, 0), translated->stride);
531 
532     size_t totalCount = gl::ComputeVertexBindingElementCount(
533         binding.getDivisor(), static_cast<size_t>(count), static_cast<size_t>(instances));
534 
535     ANGLE_TRY(mStreamingBuffer->storeDynamicAttribute(
536         attrib, binding, translated->currentValueType, firstVertexIndex,
537         static_cast<GLsizei>(totalCount), instances, &streamOffset, sourceData));
538 
539     VertexBuffer *vertexBuffer = mStreamingBuffer->getVertexBuffer();
540 
541     translated->vertexBuffer.set(vertexBuffer);
542     translated->serial = vertexBuffer->getSerial();
543     translated->baseOffset            = streamOffset;
544     translated->usesFirstVertexOffset = false;
545 
546     return gl::NoError();
547 }
548 
storeCurrentValue(const gl::VertexAttribCurrentValueData & currentValue,TranslatedAttribute * translated,size_t attribIndex)549 gl::Error VertexDataManager::storeCurrentValue(const gl::VertexAttribCurrentValueData &currentValue,
550                                                TranslatedAttribute *translated,
551                                                size_t attribIndex)
552 {
553     CurrentValueState *cachedState = &mCurrentValueCache[attribIndex];
554     auto &buffer                   = cachedState->buffer;
555 
556     if (!buffer)
557     {
558         buffer.reset(new StreamingVertexBufferInterface(mFactory, CONSTANT_VERTEX_BUFFER_SIZE));
559     }
560 
561     if (cachedState->data != currentValue)
562     {
563         ASSERT(translated->attribute && translated->binding);
564         const auto &attrib  = *translated->attribute;
565         const auto &binding = *translated->binding;
566 
567         ANGLE_TRY(buffer->reserveVertexSpace(attrib, binding, 1, 0));
568 
569         const uint8_t *sourceData = reinterpret_cast<const uint8_t*>(currentValue.FloatValues);
570         unsigned int streamOffset;
571         ANGLE_TRY(buffer->storeDynamicAttribute(attrib, binding, currentValue.Type, 0, 1, 0,
572                                                 &streamOffset, sourceData));
573 
574         buffer->getVertexBuffer()->hintUnmapResource();
575 
576         cachedState->data = currentValue;
577         cachedState->offset = streamOffset;
578     }
579 
580     translated->vertexBuffer.set(buffer->getVertexBuffer());
581 
582     translated->storage = nullptr;
583     translated->serial  = buffer->getSerial();
584     translated->divisor = 0;
585     translated->stride  = 0;
586     translated->baseOffset            = static_cast<unsigned int>(cachedState->offset);
587     translated->usesFirstVertexOffset = false;
588 
589     return gl::NoError();
590 }
591 
592 // VertexBufferBinding implementation
VertexBufferBinding()593 VertexBufferBinding::VertexBufferBinding() : mBoundVertexBuffer(nullptr)
594 {
595 }
596 
VertexBufferBinding(const VertexBufferBinding & other)597 VertexBufferBinding::VertexBufferBinding(const VertexBufferBinding &other)
598     : mBoundVertexBuffer(other.mBoundVertexBuffer)
599 {
600     if (mBoundVertexBuffer)
601     {
602         mBoundVertexBuffer->addRef();
603     }
604 }
605 
~VertexBufferBinding()606 VertexBufferBinding::~VertexBufferBinding()
607 {
608     if (mBoundVertexBuffer)
609     {
610         mBoundVertexBuffer->release();
611     }
612 }
613 
operator =(const VertexBufferBinding & other)614 VertexBufferBinding &VertexBufferBinding::operator=(const VertexBufferBinding &other)
615 {
616     mBoundVertexBuffer = other.mBoundVertexBuffer;
617     if (mBoundVertexBuffer)
618     {
619         mBoundVertexBuffer->addRef();
620     }
621     return *this;
622 }
623 
set(VertexBuffer * vertexBuffer)624 void VertexBufferBinding::set(VertexBuffer *vertexBuffer)
625 {
626     if (mBoundVertexBuffer == vertexBuffer)
627         return;
628 
629     if (mBoundVertexBuffer)
630     {
631         mBoundVertexBuffer->release();
632     }
633     if (vertexBuffer)
634     {
635         vertexBuffer->addRef();
636     }
637 
638     mBoundVertexBuffer = vertexBuffer;
639 }
640 
get() const641 VertexBuffer *VertexBufferBinding::get() const
642 {
643     return mBoundVertexBuffer;
644 }
645 
646 }  // namespace rx
647