gecko-dev/gfx/layers/mlgpu/SharedBufferMLGPU.cpp

315 строки
7.3 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SharedBufferMLGPU.h"
#include "BufferCache.h"
#include "MLGDevice.h"
using namespace std;
namespace mozilla {
namespace layers {
SharedBufferMLGPU::SharedBufferMLGPU(MLGDevice* aDevice, MLGBufferType aType, size_t aDefaultSize)
: mDevice(aDevice),
mType(aType),
mDefaultSize(aDefaultSize),
mCanUseOffsetAllocation(true),
mCurrentPosition(0),
mMaxSize(0),
mMapped(false),
mBytesUsedThisFrame(0),
mNumSmallFrames(0)
{
MOZ_COUNT_CTOR(SharedBufferMLGPU);
}
SharedBufferMLGPU::~SharedBufferMLGPU()
{
MOZ_COUNT_DTOR(SharedBufferMLGPU);
Unmap();
}
bool
SharedBufferMLGPU::Init()
{
// If we can't use buffer offset binding, we never allocated shared buffers.
if (!mCanUseOffsetAllocation) {
return true;
}
// If we can use offset binding, allocate an initial shared buffer now.
if (!GrowBuffer(mDefaultSize)) {
return false;
}
return true;
}
void
SharedBufferMLGPU::Reset()
{
// We shouldn't be mapped here, but just in case, unmap now.
Unmap();
mBytesUsedThisFrame = 0;
// If we allocated a large buffer for a particularly heavy layer tree,
// but have not used most of the buffer again for many frames, we
// discard the buffer. This is to prevent having to perform large
// pointless uploads after visiting a single havy page - it also
// lessens ping-ponging between large and small buffers.
if (mBuffer &&
(mBuffer->GetSize() > mDefaultSize * 4) &&
mNumSmallFrames >= 10)
{
mBuffer = nullptr;
}
// Note that we do not aggressively map a new buffer. There's no reason to,
// and it'd cause unnecessary uploads when painting empty frames.
}
bool
SharedBufferMLGPU::EnsureMappedBuffer(size_t aBytes)
{
if (!mBuffer || (mMaxSize - mCurrentPosition < aBytes)) {
if (!GrowBuffer(aBytes)) {
return false;
}
}
if (!mMapped && !Map()) {
return false;
}
return true;
}
// We don't want to cache large buffers, since it results in larger uploads
// that might not be needed.
static const size_t kMaxCachedBufferSize = 128 * 1024;
bool
SharedBufferMLGPU::GrowBuffer(size_t aBytes)
{
// We only pre-allocate buffers if we can use offset allocation.
MOZ_ASSERT(mCanUseOffsetAllocation);
// Unmap the previous buffer. This will retain mBuffer, but free up the
// address space used by its mapping.
Unmap();
size_t maybeSize = mDefaultSize;
if (mBuffer) {
// Try to first grow the previous allocation size.
maybeSize = std::min(kMaxCachedBufferSize, mBuffer->GetSize() * 2);
}
size_t bytes = std::max(aBytes, maybeSize);
mBuffer = mDevice->CreateBuffer(mType, bytes, MLGUsage::Dynamic);
if (!mBuffer) {
return false;
}
mCurrentPosition = 0;
mMaxSize = mBuffer->GetSize();
return true;
}
void
SharedBufferMLGPU::PrepareForUsage()
{
Unmap();
if (mBytesUsedThisFrame <= mDefaultSize) {
mNumSmallFrames++;
} else {
mNumSmallFrames = 0;
}
}
bool
SharedBufferMLGPU::Map()
{
MOZ_ASSERT(mBuffer);
MOZ_ASSERT(!mMapped);
if (!mDevice->Map(mBuffer, MLGMapType::WRITE_DISCARD, &mMap)) {
// Don't retain the buffer, it's useless if we can't map it.
mBuffer = nullptr;
return false;
}
mCurrentPosition = 0;
mMapped = true;
return true;
}
void
SharedBufferMLGPU::Unmap()
{
if (!mMapped) {
return;
}
mBytesUsedThisFrame += mCurrentPosition;
mDevice->Unmap(mBuffer);
mMap = MLGMappedResource();
mMapped = false;
}
uint8_t*
SharedBufferMLGPU::GetBufferPointer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer)
{
if (!EnsureMappedBuffer(aBytes)) {
return nullptr;
}
ptrdiff_t newPos = mCurrentPosition + aBytes;
MOZ_ASSERT(size_t(newPos) <= mMaxSize);
*aOutOffset = mCurrentPosition;
*aOutBuffer = mBuffer;
uint8_t* ptr = reinterpret_cast<uint8_t*>(mMap.mData) + mCurrentPosition;
mCurrentPosition = newPos;
return ptr;
}
VertexBufferSection::VertexBufferSection()
: mOffset(-1),
mNumVertices(0),
mStride(0)
{}
void
VertexBufferSection::Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aNumVertices, size_t aStride)
{
mBuffer = aBuffer;
mOffset = aOffset;
mNumVertices = aNumVertices;
mStride = aStride;
}
ConstantBufferSection::ConstantBufferSection()
: mOffset(-1)
{}
void
ConstantBufferSection::Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aBytes, size_t aNumItems)
{
mBuffer = aBuffer;
mOffset = aOffset;
mNumBytes = aBytes;
mNumItems = aNumItems;
}
SharedVertexBuffer::SharedVertexBuffer(MLGDevice* aDevice, size_t aDefaultSize)
: SharedBufferMLGPU(aDevice, MLGBufferType::Vertex, aDefaultSize)
{
}
bool
SharedVertexBuffer::Allocate(VertexBufferSection* aHolder,
size_t aNumItems,
size_t aSizeOfItem,
const void* aData)
{
RefPtr<MLGBuffer> buffer;
ptrdiff_t offset;
size_t bytes = aSizeOfItem * aNumItems;
uint8_t* ptr = GetBufferPointer(bytes, &offset, &buffer);
if (!ptr) {
return false;
}
memcpy(ptr, aData, bytes);
aHolder->Init(buffer, offset, aNumItems, aSizeOfItem);
return true;
}
AutoBufferUploadBase::AutoBufferUploadBase()
: mPtr(nullptr)
{
}
AutoBufferUploadBase::~AutoBufferUploadBase()
{
if (mBuffer) {
UnmapBuffer();
}
}
void
AutoBufferUploadBase::Init(void* aPtr, MLGDevice* aDevice, MLGBuffer* aBuffer)
{
MOZ_ASSERT(!mPtr && aPtr);
mPtr = aPtr;
mDevice = aDevice;
mBuffer = aBuffer;
}
SharedConstantBuffer::SharedConstantBuffer(MLGDevice* aDevice, size_t aDefaultSize)
: SharedBufferMLGPU(aDevice, MLGBufferType::Constant, aDefaultSize)
{
mMaxConstantBufferBindSize = aDevice->GetMaxConstantBufferBindSize();
mCanUseOffsetAllocation = aDevice->CanUseConstantBufferOffsetBinding();
}
bool
SharedConstantBuffer::Allocate(ConstantBufferSection* aHolder,
AutoBufferUploadBase* aPtr,
size_t aNumItems,
size_t aSizeOfItem)
{
MOZ_ASSERT(aSizeOfItem % 16 == 0, "Items must be padded to 16 bytes");
size_t bytes = aNumItems * aSizeOfItem;
if (bytes > mMaxConstantBufferBindSize) {
gfxWarning() << "Attempted to allocate too many bytes into a constant buffer";
return false;
}
RefPtr<MLGBuffer> buffer;
ptrdiff_t offset;
if (!GetBufferPointer(aPtr, bytes, &offset, &buffer)) {
return false;
}
aHolder->Init(buffer, offset, bytes, aNumItems);
return true;
}
uint8_t*
SharedConstantBuffer::AllocateNewBuffer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer)
{
RefPtr<MLGBuffer> buffer;
if (BufferCache* cache = mDevice->GetConstantBufferCache()) {
buffer = cache->GetOrCreateBuffer(aBytes);
} else {
buffer = mDevice->CreateBuffer(MLGBufferType::Constant, aBytes, MLGUsage::Dynamic);
}
if (!buffer) {
return nullptr;
}
MLGMappedResource map;
if (!mDevice->Map(buffer, MLGMapType::WRITE_DISCARD, &map)) {
return nullptr;
}
// Signal that offsetting is not supported.
*aOutOffset = -1;
*aOutBuffer = buffer;
return reinterpret_cast<uint8_t*>(map.mData);
}
void
AutoBufferUploadBase::UnmapBuffer()
{
mDevice->Unmap(mBuffer);
}
} // namespace layers
} // namespace mozilla