| // | |
| // Copyright 2014 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // BufferD3D.cpp Defines common functionality between the Buffer9 and Buffer11 classes. | |
| #include "libANGLE/renderer/d3d/BufferD3D.h" | |
| #include "common/mathutil.h" | |
| #include "common/utilities.h" | |
| #include "libANGLE/renderer/d3d/IndexBuffer.h" | |
| #include "libANGLE/renderer/d3d/RendererD3D.h" | |
| #include "libANGLE/renderer/d3d/VertexBuffer.h" | |
| namespace rx | |
| { | |
| unsigned int BufferD3D::mNextSerial = 1; | |
| BufferD3D::BufferD3D(const gl::BufferState &state, BufferFactoryD3D *factory) | |
| : BufferImpl(state), | |
| mFactory(factory), | |
| mStaticIndexBuffer(nullptr), | |
| mStaticBufferCacheTotalSize(0), | |
| mStaticVertexBufferOutOfDate(false), | |
| mUnmodifiedDataUse(0), | |
| mUsage(D3DBufferUsage::STATIC) | |
| { | |
| updateSerial(); | |
| } | |
| BufferD3D::~BufferD3D() | |
| { | |
| SafeDelete(mStaticIndexBuffer); | |
| } | |
| void BufferD3D::emptyStaticBufferCache() | |
| { | |
| mStaticVertexBuffers.clear(); | |
| mStaticBufferCacheTotalSize = 0; | |
| } | |
| void BufferD3D::updateSerial() | |
| { | |
| mSerial = mNextSerial++; | |
| } | |
| void BufferD3D::updateD3DBufferUsage(const gl::Context *context, gl::BufferUsage usage) | |
| { | |
| switch (usage) | |
| { | |
| case gl::BufferUsage::StaticCopy: | |
| case gl::BufferUsage::StaticDraw: | |
| case gl::BufferUsage::StaticRead: | |
| case gl::BufferUsage::DynamicCopy: | |
| case gl::BufferUsage::DynamicRead: | |
| case gl::BufferUsage::StreamCopy: | |
| case gl::BufferUsage::StreamRead: | |
| mUsage = D3DBufferUsage::STATIC; | |
| initializeStaticData(context); | |
| break; | |
| case gl::BufferUsage::DynamicDraw: | |
| case gl::BufferUsage::StreamDraw: | |
| mUsage = D3DBufferUsage::DYNAMIC; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| void BufferD3D::initializeStaticData(const gl::Context *context) | |
| { | |
| if (mStaticVertexBuffers.empty()) | |
| { | |
| StaticVertexBufferInterface *newStaticBuffer = new StaticVertexBufferInterface(mFactory); | |
| mStaticVertexBuffers.push_back( | |
| std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer)); | |
| } | |
| if (!mStaticIndexBuffer) | |
| { | |
| mStaticIndexBuffer = new StaticIndexBufferInterface(mFactory); | |
| } | |
| } | |
| StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer() | |
| { | |
| return mStaticIndexBuffer; | |
| } | |
| StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer(const gl::VertexAttribute &attribute, | |
| const gl::VertexBinding &binding) | |
| { | |
| if (mStaticVertexBuffers.empty()) | |
| { | |
| // Early out if there aren't any static buffers at all | |
| return nullptr; | |
| } | |
| // Early out, the attribute can be added to mStaticVertexBuffer. | |
| if (mStaticVertexBuffers.size() == 1 && mStaticVertexBuffers[0]->empty()) | |
| { | |
| return mStaticVertexBuffers[0].get(); | |
| } | |
| // Cache size limiting: track the total allocated buffer sizes. | |
| size_t currentTotalSize = 0; | |
| // At this point, see if any of the existing static buffers contains the attribute data | |
| // If there is a cached static buffer that already contains the attribute, then return it | |
| for (const auto &staticBuffer : mStaticVertexBuffers) | |
| { | |
| if (staticBuffer->matchesAttribute(attribute, binding)) | |
| { | |
| return staticBuffer.get(); | |
| } | |
| currentTotalSize += staticBuffer->getBufferSize(); | |
| } | |
| // Cache size limiting: Clean-up threshold is four times the base buffer size, with a minimum. | |
| ASSERT(getSize() < std::numeric_limits<size_t>::max() / 4u); | |
| size_t sizeThreshold = std::max(getSize() * 4u, static_cast<size_t>(0x1000u)); | |
| // If we're past the threshold, clear the buffer cache. Note that this will release buffers | |
| // that are currenly bound, and in an edge case can even translate the same attribute twice | |
| // in the same draw call. It will not delete currently bound buffers, however, because they | |
| // are ref counted. | |
| if (currentTotalSize > sizeThreshold) | |
| { | |
| emptyStaticBufferCache(); | |
| } | |
| // At this point, we must create a new static buffer for the attribute data. | |
| StaticVertexBufferInterface *newStaticBuffer = new StaticVertexBufferInterface(mFactory); | |
| newStaticBuffer->setAttribute(attribute, binding); | |
| mStaticVertexBuffers.push_back(std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer)); | |
| return newStaticBuffer; | |
| } | |
| void BufferD3D::invalidateStaticData(const gl::Context *context) | |
| { | |
| emptyStaticBufferCache(); | |
| if (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0) | |
| { | |
| SafeDelete(mStaticIndexBuffer); | |
| } | |
| // If the buffer was created with a static usage then we recreate the static | |
| // buffers so that they are populated the next time we use this buffer. | |
| if (mUsage == D3DBufferUsage::STATIC) | |
| { | |
| initializeStaticData(context); | |
| } | |
| mUnmodifiedDataUse = 0; | |
| } | |
| // Creates static buffers if sufficient used data has been left unmodified | |
| void BufferD3D::promoteStaticUsage(const gl::Context *context, size_t dataSize) | |
| { | |
| if (mUsage == D3DBufferUsage::DYNAMIC) | |
| { | |
| // Note: This is not a safe math operation. 'dataSize' can come from the app. | |
| mUnmodifiedDataUse += dataSize; | |
| if (mUnmodifiedDataUse > 3 * getSize()) | |
| { | |
| updateD3DBufferUsage(context, gl::BufferUsage::StaticDraw); | |
| } | |
| } | |
| } | |
| angle::Result BufferD3D::getIndexRange(const gl::Context *context, | |
| gl::DrawElementsType type, | |
| size_t offset, | |
| size_t count, | |
| bool primitiveRestartEnabled, | |
| gl::IndexRange *outRange) | |
| { | |
| const uint8_t *data = nullptr; | |
| ANGLE_TRY(getData(context, &data)); | |
| *outRange = gl::ComputeIndexRange(type, data + offset, count, primitiveRestartEnabled); | |
| return angle::Result::Continue; | |
| } | |
| } // namespace rx |