Fix sparse texture deallocation

This commit is contained in:
Brad Davis 2016-09-19 11:29:27 -07:00
parent 64cb45240d
commit 5734332150
4 changed files with 60 additions and 38 deletions

View file

@ -593,7 +593,23 @@ void GLBackend::releaseQuery(GLuint id) const {
_queriesTrash.push_back(id); _queriesTrash.push_back(id);
} }
void GLBackend::releaseLambda(const std::function<void()> lambda) const {
Lock lock(_trashMutex);
_lambdasTrash.push_back(lambda);
}
void GLBackend::recycle() const { void GLBackend::recycle() const {
{
std::list<std::function<void()>> lamdbasTrash;
{
Lock lock(_trashMutex);
std::swap(_lambdasTrash, lamdbasTrash);
}
for (auto lambda : lamdbasTrash) {
lambda();
}
}
{ {
std::vector<GLuint> ids; std::vector<GLuint> ids;
std::list<std::pair<GLuint, Size>> buffersTrash; std::list<std::pair<GLuint, Size>> buffersTrash;

View file

@ -175,6 +175,7 @@ public:
virtual void releaseShader(GLuint id) const; virtual void releaseShader(GLuint id) const;
virtual void releaseProgram(GLuint id) const; virtual void releaseProgram(GLuint id) const;
virtual void releaseQuery(GLuint id) const; virtual void releaseQuery(GLuint id) const;
virtual void releaseLambda(const std::function<void()> lambda) const;
protected: protected:
@ -197,6 +198,7 @@ protected:
mutable std::list<GLuint> _shadersTrash; mutable std::list<GLuint> _shadersTrash;
mutable std::list<GLuint> _programsTrash; mutable std::list<GLuint> _programsTrash;
mutable std::list<GLuint> _queriesTrash; mutable std::list<GLuint> _queriesTrash;
mutable std::list<std::function<void()>> _lambdasTrash;
void renderPassTransfer(const Batch& batch); void renderPassTransfer(const Batch& batch);
void renderPassDraw(const Batch& batch); void renderPassDraw(const Batch& batch);

View file

@ -155,7 +155,6 @@ GLTexture::~GLTexture() {
auto backend = _backend.lock(); auto backend = _backend.lock();
if (backend) { if (backend) {
backend->releaseTexture(_id, _size); backend->releaseTexture(_id, _size);
backend->recycle();
} }
} }
Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0);

View file

@ -30,6 +30,7 @@ static bool enableSparseTextures = !QProcessEnvironment::systemEnvironment().con
// Allocate 1 MB of buffer space for paged transfers // Allocate 1 MB of buffer space for paged transfers
#define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024)
#define DEFAULT_GL_PIXEL_ALIGNMENT 4
using GL45Texture = GL45Backend::GL45Texture; using GL45Texture = GL45Backend::GL45Texture;
@ -39,7 +40,6 @@ using TextureTypeFormat = std::pair<GLenum, GLenum>;
std::map<TextureTypeFormat, std::vector<uvec3>> sparsePageDimensionsByFormat; std::map<TextureTypeFormat, std::vector<uvec3>> sparsePageDimensionsByFormat;
Mutex sparsePageDimensionsByFormatMutex; Mutex sparsePageDimensionsByFormatMutex;
static std::vector<uvec3> getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { static std::vector<uvec3> getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) {
{ {
Lock lock(sparsePageDimensionsByFormatMutex); Lock lock(sparsePageDimensionsByFormatMutex);
@ -64,7 +64,7 @@ static std::vector<uvec3> getPageDimensionsForFormat(const TextureTypeFormat& ty
for (GLint i = 0; i < count; ++i) { for (GLint i = 0; i < count; ++i) {
result[i] = uvec3(x[i], y[i], z[i]); result[i] = uvec3(x[i], y[i], z[i]);
} }
qDebug() << "Got " << count << " page sizes"; qCDebug(gpugl45logging) << "Got " << count << " page sizes";
} }
{ {
@ -212,7 +212,6 @@ bool TransferState::increment() {
return false; return false;
} }
#define DEFAULT_GL_PIXEL_ALIGNMENT 4
void TransferState::populatePage(std::vector<uint8_t>& buffer) { void TransferState::populatePage(std::vector<uint8_t>& buffer) {
uvec3 pageSize = currentPageSize(); uvec3 pageSize = currentPageSize();
auto bytesPerPageLine = _bytesPerPixel * pageSize.x; auto bytesPerPageLine = _bytesPerPixel * pageSize.x;
@ -254,35 +253,41 @@ GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture&
} }
} }
// Destructors get called on the main thread, potentially without a context active. We need to queue the
// deallocation of the sparse pages for this content.
GL45Texture::~GL45Texture() { GL45Texture::~GL45Texture() {
if (_sparseInfo._sparse) {
auto mipLevels = usedMipLevels();
{ if (_sparseInfo._sparse) {
Lock lock(texturesByMipCountsMutex); auto backend = _backend.lock();
if (texturesByMipCounts.count(mipLevels)) { if (backend) {
auto& textures = texturesByMipCounts[mipLevels]; auto id = _id;
textures.erase(this); auto mipLevels = usedMipLevels();
if (textures.empty()) { {
texturesByMipCounts.erase(mipLevels); Lock lock(texturesByMipCountsMutex);
if (texturesByMipCounts.count(mipLevels)) {
auto& textures = texturesByMipCounts[mipLevels];
textures.erase(this);
if (textures.empty()) {
texturesByMipCounts.erase(mipLevels);
}
} }
} }
}
auto maxSparseMip = std::min<uint16_t>(_maxMip, _sparseInfo._maxSparseLevel); auto maxSparseMip = std::min<uint16_t>(_maxMip, _sparseInfo._maxSparseLevel);
uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) {
auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel);
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions); backend->releaseLambda([=] {
for (uint8_t face = 0; face < maxFace; ++face) { glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE);
glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, mipDimensions.x, mipDimensions.y, mipDimensions.z, GL_FALSE); });
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
assert(deallocatedPages <= _allocatedPages); assert(deallocatedPages <= _allocatedPages);
_allocatedPages -= deallocatedPages; _allocatedPages -= deallocatedPages;
} }
}
if (0 != _allocatedPages) { if (0 != _allocatedPages) {
qWarning() << "Allocated pages remaining " << _id << " " << _allocatedPages; qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _allocatedPages;
}
} }
} }
} }
@ -292,7 +297,7 @@ void GL45Texture::withPreservedTexture(std::function<void()> f) const {
} }
void GL45Texture::generateMips() const { void GL45Texture::generateMips() const {
qDebug() << "Generating mipmaps for " << _gpuObject.source().c_str(); qCDebug(gpugl45logging) << "Generating mipmaps for " << _gpuObject.source().c_str();
glGenerateTextureMipmap(_id); glGenerateTextureMipmap(_id);
(void)CHECK_GL_ERROR(); (void)CHECK_GL_ERROR();
} }
@ -337,7 +342,7 @@ bool GL45Texture::continueTransfer() {
if (_sparseInfo._sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { if (_sparseInfo._sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) {
if (_allocatedPages > _sparseInfo._maxPages) { if (_allocatedPages > _sparseInfo._maxPages) {
qWarning() << "Exceeded max page allocation!"; qCWarning(gpugl45logging) << "Exceeded max page allocation!";
} }
glTexturePageCommitmentEXT(_id, _transferState._mipLevel, glTexturePageCommitmentEXT(_id, _transferState._mipLevel,
offset.x, offset.y, _transferState._face, offset.x, offset.y, _transferState._face,
@ -373,7 +378,7 @@ bool GL45Texture::continueTransfer() {
auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions); auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions);
auto newPages = _allocatedPages - _lastMipAllocatedPages; auto newPages = _allocatedPages - _lastMipAllocatedPages;
if (newPages != mipExpectedPages) { if (newPages != mipExpectedPages) {
qWarning() << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages; qCWarning(gpugl45logging) << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages;
} }
_lastMipAllocatedPages = _allocatedPages; _lastMipAllocatedPages = _allocatedPages;
} }
@ -426,12 +431,12 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
} }
if (newMinMip < _minMip) { if (newMinMip < _minMip) {
qWarning() << "Cannot decrease the min mip"; qCWarning(gpugl45logging) << "Cannot decrease the min mip";
return; return;
} }
if (newMinMip > _sparseInfo._maxSparseLevel) { if (newMinMip > _sparseInfo._maxSparseLevel) {
qWarning() << "Cannot increase the min mip into the mip tail"; qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail";
return; return;
} }
@ -448,7 +453,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
// If we weren't generating mips before, we need to now that we're stripping down mip levels. // If we weren't generating mips before, we need to now that we're stripping down mip levels.
if (!_gpuObject.isAutogenerateMips()) { if (!_gpuObject.isAutogenerateMips()) {
qDebug() << "Force mip generation for texture"; qCDebug(gpugl45logging) << "Force mip generation for texture";
glGenerateTextureMipmap(_id); glGenerateTextureMipmap(_id);
} }
@ -474,9 +479,9 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
updateSize(); updateSize();
size_t newSize = _size; size_t newSize = _size;
if (newSize > oldSize) { if (newSize > oldSize) {
qDebug() << "WTF"; qCDebug(gpugl45logging) << "WTF";
qDebug() << "\told size " << oldSize; qCDebug(gpugl45logging) << "\told size " << oldSize;
qDebug() << "\tnew size " << newSize; qCDebug(gpugl45logging) << "\tnew size " << newSize;
} }
// Re-insert into the texture-by-mips map if appropriate // Re-insert into the texture-by-mips map if appropriate
@ -512,18 +517,18 @@ void GL45Backend::derezTextures() const {
Lock lock(texturesByMipCountsMutex); Lock lock(texturesByMipCountsMutex);
if (texturesByMipCounts.empty()) { if (texturesByMipCounts.empty()) {
qDebug() << "No available textures to derez"; qCDebug(gpugl45logging) << "No available textures to derez";
return; return;
} }
auto mipLevel = texturesByMipCounts.rbegin()->first; auto mipLevel = texturesByMipCounts.rbegin()->first;
if (mipLevel <= 1) { if (mipLevel <= 1) {
qDebug() << "Max mip levels " << mipLevel; qCDebug(gpugl45logging) << "Max mip levels " << mipLevel;
return; return;
} }
qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); qCDebug(gpugl45logging) << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage();
qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); qCDebug(gpugl45logging) << "Used texture memory " << Context::getTextureGPUMemoryUsage();
GL45Texture* targetTexture = nullptr; GL45Texture* targetTexture = nullptr;
{ {
@ -533,5 +538,5 @@ void GL45Backend::derezTextures() const {
} }
lock.unlock(); lock.unlock();
targetTexture->derez(); targetTexture->derez();
qDebug() << "New Used texture memory " << Context::getTextureGPUMemoryUsage(); qCDebug(gpugl45logging) << "New Used texture memory " << Context::getTextureGPUMemoryUsage();
} }