mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 02:36:54 +02:00
Attempt to account for sparse texture memory overhead
This commit is contained in:
parent
479aa93ab2
commit
70664b64eb
2 changed files with 21 additions and 183 deletions
|
@ -48,39 +48,18 @@ public:
|
||||||
void update();
|
void update();
|
||||||
uvec3 getPageCounts(const uvec3& dimensions) const;
|
uvec3 getPageCounts(const uvec3& dimensions) const;
|
||||||
uint32_t getPageCount(const uvec3& dimensions) const;
|
uint32_t getPageCount(const uvec3& dimensions) const;
|
||||||
|
uint32_t getSize() const;
|
||||||
|
|
||||||
GL45Texture& texture;
|
GL45Texture& texture;
|
||||||
bool sparse { false };
|
bool sparse { false };
|
||||||
uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION };
|
uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION };
|
||||||
GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL };
|
GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL };
|
||||||
|
uint32_t allocatedPages { 0 };
|
||||||
uint32_t maxPages { 0 };
|
uint32_t maxPages { 0 };
|
||||||
uint32_t pageBytes { 0 };
|
uint32_t pageBytes { 0 };
|
||||||
GLint pageDimensionsIndex { 0 };
|
GLint pageDimensionsIndex { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#if INCREMENTAL_TRANSFER
|
|
||||||
struct TransferState {
|
|
||||||
TransferState(GL45Texture& texture);
|
|
||||||
uvec3 currentPageSize() const;
|
|
||||||
void updateMip();
|
|
||||||
void populatePage(std::vector<uint8_t>& dest);
|
|
||||||
bool increment();
|
|
||||||
|
|
||||||
GL45Texture& texture;
|
|
||||||
GLTexelFormat texelFormat;
|
|
||||||
uint8_t face { 0 };
|
|
||||||
uint16_t mipLevel { 0 };
|
|
||||||
uint32_t bytesPerLine { 0 };
|
|
||||||
uint32_t bytesPerPixel { 0 };
|
|
||||||
uint32_t bytesPerPage { 0 };
|
|
||||||
uvec3 mipDimensions;
|
|
||||||
uvec3 mipOffset;
|
|
||||||
const uint8_t* srcPointer { nullptr };
|
|
||||||
};
|
|
||||||
protected:
|
|
||||||
TransferState _transferState;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateMips() override;
|
void updateMips() override;
|
||||||
void stripToMip(uint16_t newMinMip);
|
void stripToMip(uint16_t newMinMip);
|
||||||
|
@ -98,8 +77,6 @@ public:
|
||||||
void derez();
|
void derez();
|
||||||
|
|
||||||
SparseInfo _sparseInfo;
|
SparseInfo _sparseInfo;
|
||||||
uint32_t _allocatedPages { 0 };
|
|
||||||
uint32_t _lastMipAllocatedPages { 0 };
|
|
||||||
uint16_t _mipOffset { 0 };
|
uint16_t _mipOffset { 0 };
|
||||||
friend class GL45Backend;
|
friend class GL45Backend;
|
||||||
};
|
};
|
||||||
|
|
|
@ -116,6 +116,8 @@ void SparseInfo::maybeMakeSparse() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f
|
||||||
|
|
||||||
// This can only be called after we've established our storage size
|
// This can only be called after we've established our storage size
|
||||||
void SparseInfo::update() {
|
void SparseInfo::update() {
|
||||||
if (!sparse) {
|
if (!sparse) {
|
||||||
|
@ -124,6 +126,9 @@ void SparseInfo::update() {
|
||||||
glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel);
|
glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel);
|
||||||
pageBytes = texture._gpuObject.getTexelFormat().getSize();
|
pageBytes = texture._gpuObject.getTexelFormat().getSize();
|
||||||
pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z;
|
pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z;
|
||||||
|
// Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for
|
||||||
|
// sparse textures as compared to non-sparse, so we acount for that here.
|
||||||
|
pageBytes = (uint32_t)(pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE);
|
||||||
|
|
||||||
for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) {
|
for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) {
|
||||||
auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
|
auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
|
||||||
|
@ -146,6 +151,11 @@ uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const {
|
||||||
return pageCounts.x * pageCounts.y * pageCounts.z;
|
return pageCounts.x * pageCounts.y * pageCounts.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t SparseInfo::getSize() const {
|
||||||
|
return allocatedPages * pageBytes;
|
||||||
|
}
|
||||||
|
|
||||||
void GL45Backend::initTextureManagementStage() {
|
void GL45Backend::initTextureManagementStage() {
|
||||||
// enable the Sparse Texture on gl45
|
// enable the Sparse Texture on gl45
|
||||||
_textureManagement._sparseCapable = true;
|
_textureManagement._sparseCapable = true;
|
||||||
|
@ -160,93 +170,6 @@ void GL45Backend::initTextureManagementStage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INCREMENTAL_TRANSFER
|
|
||||||
|
|
||||||
using TransferState = GL45Backend::GL45Texture::TransferState;
|
|
||||||
|
|
||||||
TransferState::TransferState(GL45Texture& texture) : texture(texture) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransferState::updateMip() {
|
|
||||||
mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
|
|
||||||
mipOffset = uvec3();
|
|
||||||
if (!texture._gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
|
|
||||||
srcPointer = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto mip = texture._gpuObject.accessStoredMipFace(mipLevel, face);
|
|
||||||
texelFormat = gl::GLTexelFormat::evalGLTexelFormat(texture._gpuObject.getTexelFormat(), mip->getFormat());
|
|
||||||
srcPointer = mip->readData();
|
|
||||||
bytesPerLine = (uint32_t)mip->getSize() / mipDimensions.y;
|
|
||||||
bytesPerPixel = bytesPerLine / mipDimensions.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TransferState::increment() {
|
|
||||||
const SparseInfo& sparse = texture._sparseInfo;
|
|
||||||
if ((mipOffset.x + sparse.pageDimensions.x) < mipDimensions.x) {
|
|
||||||
mipOffset.x += sparse.pageDimensions.x;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((mipOffset.y + sparse.pageDimensions.y) < mipDimensions.y) {
|
|
||||||
mipOffset.x = 0;
|
|
||||||
mipOffset.y += sparse.pageDimensions.y;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mipOffset.z + sparse.pageDimensions.z < mipDimensions.z) {
|
|
||||||
mipOffset.x = 0;
|
|
||||||
mipOffset.y = 0;
|
|
||||||
++mipOffset.z;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done with this mip?, move on to the next mip
|
|
||||||
if (mipLevel + 1 < texture.usedMipLevels()) {
|
|
||||||
mipOffset = uvec3(0);
|
|
||||||
++mipLevel;
|
|
||||||
updateMip();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t maxFace = (uint8_t)((texture._target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
|
|
||||||
uint8_t nextFace = face + 1;
|
|
||||||
// Done with this face? Move on to the next
|
|
||||||
if (nextFace < maxFace) {
|
|
||||||
++face;
|
|
||||||
mipOffset = uvec3(0);
|
|
||||||
mipLevel = 0;
|
|
||||||
updateMip();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransferState::populatePage(std::vector<uint8_t>& buffer) {
|
|
||||||
uvec3 pageSize = currentPageSize();
|
|
||||||
auto bytesPerPageLine = bytesPerPixel * pageSize.x;
|
|
||||||
if (0 != (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT)) {
|
|
||||||
bytesPerPageLine += DEFAULT_GL_PIXEL_ALIGNMENT - (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT);
|
|
||||||
assert(0 == (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT));
|
|
||||||
}
|
|
||||||
auto totalPageSize = bytesPerPageLine * pageSize.y;
|
|
||||||
if (totalPageSize > buffer.size()) {
|
|
||||||
buffer.resize(totalPageSize);
|
|
||||||
}
|
|
||||||
uint8_t* dst = &buffer[0];
|
|
||||||
for (uint32_t y = 0; y < pageSize.y; ++y) {
|
|
||||||
uint32_t srcOffset = (bytesPerLine * (mipOffset.y + y)) + (bytesPerPixel * mipOffset.x);
|
|
||||||
uint32_t dstOffset = bytesPerPageLine * y;
|
|
||||||
memcpy(dst + dstOffset, srcPointer + srcOffset, pageSize.x * bytesPerPixel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uvec3 TransferState::currentPageSize() const {
|
|
||||||
return glm::clamp(mipDimensions - mipOffset, uvec3(1), texture._sparseInfo.pageDimensions);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GLuint GL45Texture::allocate(const Texture& texture) {
|
GLuint GL45Texture::allocate(const Texture& texture) {
|
||||||
GLuint result;
|
GLuint result;
|
||||||
|
@ -260,17 +183,11 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) {
|
||||||
|
|
||||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
|
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
|
||||||
: GLTexture(backend, texture, externalId), _sparseInfo(*this)
|
: GLTexture(backend, texture, externalId), _sparseInfo(*this)
|
||||||
#if INCREMENTAL_TRANSFER
|
|
||||||
, _transferState(*this)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
|
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
|
||||||
: GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this)
|
: GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this)
|
||||||
#if INCREMENTAL_TRANSFER
|
|
||||||
, _transferState(*this)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
|
|
||||||
auto theBackend = _backend.lock();
|
auto theBackend = _backend.lock();
|
||||||
|
@ -316,12 +233,12 @@ GL45Texture::~GL45Texture() {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
||||||
assert(deallocatedPages <= _allocatedPages);
|
assert(deallocatedPages <= _sparseInfo.allocatedPages);
|
||||||
_allocatedPages -= deallocatedPages;
|
_sparseInfo.allocatedPages -= deallocatedPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 != _allocatedPages) {
|
if (0 != _sparseInfo.allocatedPages) {
|
||||||
qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _allocatedPages;
|
qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto size = _size;
|
auto size = _size;
|
||||||
|
@ -365,9 +282,9 @@ void GL45Texture::updateSize() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transferrable && _sparseInfo.sparse) {
|
if (_transferrable && _sparseInfo.sparse) {
|
||||||
auto size = _allocatedPages * _sparseInfo.pageBytes;
|
auto size = _sparseInfo.getSize();
|
||||||
Backend::updateTextureGPUSparseMemoryUsage(_size, size);
|
Backend::updateTextureGPUSparseMemoryUsage(_size, size);
|
||||||
setSize(_allocatedPages * _sparseInfo.pageBytes);
|
setSize(size);
|
||||||
} else {
|
} else {
|
||||||
setSize(_gpuObject.evalTotalSize(_mipOffset));
|
setSize(_gpuObject.evalTotalSize(_mipOffset));
|
||||||
}
|
}
|
||||||
|
@ -376,20 +293,16 @@ void GL45Texture::updateSize() const {
|
||||||
void GL45Texture::startTransfer() {
|
void GL45Texture::startTransfer() {
|
||||||
Parent::startTransfer();
|
Parent::startTransfer();
|
||||||
_sparseInfo.update();
|
_sparseInfo.update();
|
||||||
#if INCREMENTAL_TRANSFER
|
|
||||||
_transferState.updateMip();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GL45Texture::continueTransfer() {
|
bool GL45Texture::continueTransfer() {
|
||||||
#if !INCREMENTAL_TRANSFER
|
|
||||||
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
|
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
|
||||||
for (uint8_t face = 0; face < maxFace; ++face) {
|
for (uint8_t face = 0; face < maxFace; ++face) {
|
||||||
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
|
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
|
||||||
auto size = _gpuObject.evalMipDimensions(mipLevel);
|
auto size = _gpuObject.evalMipDimensions(mipLevel);
|
||||||
if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) {
|
if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) {
|
||||||
glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE);
|
glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE);
|
||||||
_allocatedPages += _sparseInfo.getPageCount(size);
|
_sparseInfo.allocatedPages += _sparseInfo.getPageCount(size);
|
||||||
}
|
}
|
||||||
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
|
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
|
||||||
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
|
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
|
||||||
|
@ -413,58 +326,6 @@ bool GL45Texture::continueTransfer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
#else
|
|
||||||
static std::vector<uint8_t> buffer;
|
|
||||||
if (buffer.empty()) {
|
|
||||||
buffer.resize(DEFAULT_PAGE_BUFFER_SIZE);
|
|
||||||
}
|
|
||||||
const uvec3 pageSize = _transferState.currentPageSize();
|
|
||||||
const uvec3& offset = _transferState.mipOffset;
|
|
||||||
|
|
||||||
if (_sparseInfo.sparse && _transferState.mipLevel <= _sparseInfo.maxSparseLevel) {
|
|
||||||
if (_allocatedPages > _sparseInfo.maxPages) {
|
|
||||||
qCWarning(gpugl45logging) << "Exceeded max page allocation!";
|
|
||||||
}
|
|
||||||
glTexturePageCommitmentEXT(_id, _transferState.mipLevel,
|
|
||||||
offset.x, offset.y, _transferState.face,
|
|
||||||
pageSize.x, pageSize.y, pageSize.z,
|
|
||||||
GL_TRUE);
|
|
||||||
++_allocatedPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_transferState.srcPointer) {
|
|
||||||
// Transfer the mip data
|
|
||||||
_transferState.populatePage(buffer);
|
|
||||||
if (GL_TEXTURE_2D == _target) {
|
|
||||||
glTextureSubImage2D(_id, _transferState.mipLevel,
|
|
||||||
offset.x, offset.y,
|
|
||||||
pageSize.x, pageSize.y,
|
|
||||||
_transferState.texelFormat.format, _transferState.texelFormat.type, &buffer[0]);
|
|
||||||
} else if (GL_TEXTURE_CUBE_MAP == _target) {
|
|
||||||
auto target = CUBE_FACE_LAYOUT[_transferState.face];
|
|
||||||
// DSA ARB does not work on AMD, so use EXT
|
|
||||||
// glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData());
|
|
||||||
glTextureSubImage2DEXT(_id, target, _transferState.mipLevel,
|
|
||||||
offset.x, offset.y,
|
|
||||||
pageSize.x, pageSize.y,
|
|
||||||
_transferState.texelFormat.format, _transferState.texelFormat.type, &buffer[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serverWait();
|
|
||||||
auto currentMip = _transferState.mipLevel;
|
|
||||||
auto result = _transferState.increment();
|
|
||||||
if (_sparseInfo.sparse && _transferState.mipLevel != currentMip && currentMip <= _sparseInfo.maxSparseLevel) {
|
|
||||||
auto mipDimensions = _gpuObject.evalMipDimensions(currentMip);
|
|
||||||
auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions);
|
|
||||||
auto newPages = _allocatedPages - _lastMipAllocatedPages;
|
|
||||||
if (newPages != mipExpectedPages) {
|
|
||||||
qCWarning(gpugl45logging) << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages;
|
|
||||||
}
|
|
||||||
_lastMipAllocatedPages = _allocatedPages;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Texture::finishTransfer() {
|
void GL45Texture::finishTransfer() {
|
||||||
|
@ -545,8 +406,8 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
||||||
assert(deallocatedPages < _allocatedPages);
|
assert(deallocatedPages < _sparseInfo.allocatedPages);
|
||||||
_allocatedPages -= deallocatedPages;
|
_sparseInfo.allocatedPages -= deallocatedPages;
|
||||||
}
|
}
|
||||||
_minMip = newMinMip;
|
_minMip = newMinMip;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue