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

303 строки
8.0 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/. */
#ifndef mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
#define mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
#include "ShaderDefinitionsMLGPU.h"
#include "MLGDevice.h"
#include "MLGDeviceTypes.h"
#include "StagingBuffer.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace layers {
class MLGBuffer;
class SharedBufferMLGPU
{
public:
virtual ~SharedBufferMLGPU();
bool Init();
// Call before starting a new frame.
void Reset();
// Call to finish any pending uploads.
void PrepareForUsage();
protected:
SharedBufferMLGPU(MLGDevice* aDevice, MLGBufferType aType, size_t aDefaultSize);
bool EnsureMappedBuffer(size_t aBytes);
bool GrowBuffer(size_t aBytes);
void ForgetBuffer();
bool Map();
void Unmap();
uint8_t* GetBufferPointer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer);
protected:
// Note: RefPtr here would cause a cycle. Only MLGDevice should own
// SharedBufferMLGPU objects for now.
MLGDevice* mDevice;
MLGBufferType mType;
size_t mDefaultSize;
bool mCanUseOffsetAllocation;
// When |mBuffer| is non-null, mMaxSize is the buffer size. If mapped, the
// position is between 0 and mMaxSize, otherwise it is always 0.
RefPtr<MLGBuffer> mBuffer;
ptrdiff_t mCurrentPosition;
size_t mMaxSize;
MLGMappedResource mMap;
bool mMapped;
// These are used to track how many frames come in under the default
// buffer size in a row.
size_t mBytesUsedThisFrame;
size_t mNumSmallFrames;
};
class VertexBufferSection final
{
friend class SharedVertexBuffer;
public:
VertexBufferSection();
uint32_t Stride() const {
return mStride;
}
MLGBuffer* GetBuffer() const {
return mBuffer;
}
ptrdiff_t Offset() const {
MOZ_ASSERT(IsValid());
return mOffset;
}
size_t NumVertices() const {
return mNumVertices;
}
bool IsValid() const {
return !!mBuffer;
}
protected:
void Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aNumVertices, size_t aStride);
protected:
RefPtr<MLGBuffer> mBuffer;
ptrdiff_t mOffset;
size_t mNumVertices;
size_t mStride;
};
class ConstantBufferSection final
{
friend class SharedConstantBuffer;
public:
ConstantBufferSection();
uint32_t NumConstants() const {
return NumConstantsForBytes(mNumBytes);
}
size_t NumItems() const {
return mNumItems;
}
uint32_t Offset() const {
MOZ_ASSERT(IsValid());
return mOffset / 16;
}
MLGBuffer* GetBuffer() const {
return mBuffer;
}
bool IsValid() const {
return !!mBuffer;
}
bool HasOffset() const {
return mOffset != -1;
}
protected:
static constexpr size_t NumConstantsForBytes(size_t aBytes) {
return (aBytes + ((256 - (aBytes % 256)) % 256)) / 16;
}
void Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aBytes, size_t aNumItems);
protected:
RefPtr<MLGBuffer> mBuffer;
ptrdiff_t mOffset;
size_t mNumBytes;
size_t mNumItems;
};
// Vertex buffers don't need special alignment.
typedef StagingBuffer<0> VertexStagingBuffer;
class SharedVertexBuffer final : public SharedBufferMLGPU
{
public:
SharedVertexBuffer(MLGDevice* aDevice, size_t aDefaultSize);
// Allocate a buffer that can be uploaded immediately.
bool Allocate(VertexBufferSection* aHolder, const VertexStagingBuffer& aStaging) {
return Allocate(aHolder,
aStaging.NumItems(),
aStaging.SizeOfItem(),
aStaging.GetBufferStart());
}
// Allocate a buffer that can be uploaded immediately. This is the
// direct access version, for cases where a StagingBuffer is not
// needed.
bool Allocate(VertexBufferSection* aHolder,
size_t aNumItems,
size_t aSizeOfItem,
const void* aData);
template <typename T>
bool Allocate(VertexBufferSection* aHolder, const T& aItem) {
return Allocate(aHolder, 1, sizeof(T), &aItem);
}
};
// To support older Direct3D versions, we need to support one-off MLGBuffers,
// where data is uploaded immediately rather than at the end of all batch
// preparation. We achieve this through a small helper class.
//
// Note: the unmap is not inline sincce we don't include MLGDevice.h.
class MOZ_STACK_CLASS AutoBufferUploadBase
{
public:
AutoBufferUploadBase();
~AutoBufferUploadBase();
void Init(void* aPtr) {
MOZ_ASSERT(!mPtr && aPtr);
mPtr = aPtr;
}
void Init(void* aPtr, MLGDevice* aDevice, MLGBuffer* aBuffer);
void* get() {
return const_cast<void*>(mPtr);
}
private:
void UnmapBuffer();
protected:
RefPtr<MLGDevice> mDevice;
RefPtr<MLGBuffer> mBuffer;
void* mPtr;
};
// This is a typed helper for AutoBufferUploadBase.
template <typename T>
class AutoBufferUpload : public AutoBufferUploadBase
{
public:
AutoBufferUpload()
{}
T* operator ->() const {
return reinterpret_cast<T*>(mPtr);
}
};
class SharedConstantBuffer final : public SharedBufferMLGPU
{
public:
SharedConstantBuffer(MLGDevice* aDevice, size_t aDefaultSize);
// Allocate a buffer that can be immediately uploaded.
bool Allocate(ConstantBufferSection* aHolder, const ConstantStagingBuffer& aStaging) {
MOZ_ASSERT(aStaging.NumItems() * aStaging.SizeOfItem() == aStaging.NumBytes());
return Allocate(aHolder, aStaging.NumItems(), aStaging.SizeOfItem(), aStaging.GetBufferStart());
}
// Allocate a buffer of one item that can be immediately uploaded.
template <typename T>
bool Allocate(ConstantBufferSection* aHolder, const T& aItem) {
return Allocate(aHolder, 1, sizeof(aItem), &aItem);
}
// Allocate a buffer of N items that can be immediately uploaded.
template <typename T>
bool Allocate(ConstantBufferSection* aHolder, const T* aItems, size_t aNumItems) {
return Allocate(aHolder, aNumItems, sizeof(T), aItems);
}
// Allocate a buffer that is uploaded after the caller has finished writing
// to it. This should method should generally not be used unless copying T
// is expensive, since the default immediate-upload version has an implicit
// extra copy to the GPU. This version exposes the mapped memory directly.
template <typename T>
bool Allocate(ConstantBufferSection* aHolder, AutoBufferUpload<T>* aPtr) {
MOZ_ASSERT(sizeof(T) % 16 == 0, "Items must be padded to 16 bytes");
return Allocate(aHolder, aPtr, 1, sizeof(T));
}
private:
bool Allocate(ConstantBufferSection* aHolder,
size_t aNumItems,
size_t aSizeOfItem,
const void* aData)
{
AutoBufferUploadBase ptr;
if (!Allocate(aHolder, &ptr, aNumItems, aSizeOfItem)) {
return false;
}
memcpy(ptr.get(), aData, aNumItems * aSizeOfItem);
return true;
}
bool Allocate(ConstantBufferSection* aHolder,
AutoBufferUploadBase* aPtr,
size_t aNumItems,
size_t aSizeOfItem);
bool GetBufferPointer(AutoBufferUploadBase* aPtr,
size_t aBytes,
ptrdiff_t* aOutOffset,
RefPtr<MLGBuffer>* aOutBuffer)
{
if (!mCanUseOffsetAllocation) {
uint8_t* ptr = AllocateNewBuffer(aBytes, aOutOffset, aOutBuffer);
if (!ptr) {
return false;
}
aPtr->Init(ptr, mDevice, *aOutBuffer);
return true;
}
// Align up the allocation to 256 bytes, since D3D11 requires that
// constant buffers start at multiples of 16 elements.
size_t alignedBytes = AlignUp<256>::calc(aBytes);
uint8_t* ptr = SharedBufferMLGPU::GetBufferPointer(alignedBytes, aOutOffset, aOutBuffer);
if (!ptr) {
return false;
}
aPtr->Init(ptr);
return true;
}
uint8_t* AllocateNewBuffer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer);
private:
size_t mMaxConstantBufferBindSize;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h