Merge pull request #9702 from jherico/smarter_textures

Updates to texture managment
This commit is contained in:
samcake 2017-02-17 13:40:37 -08:00 committed by GitHub
commit 6882832abd
7 changed files with 80 additions and 110 deletions

View file

@ -108,16 +108,10 @@ public:
using VoidLambdaQueue = std::queue<VoidLambda>;
using ThreadPointer = std::shared_ptr<std::thread>;
const GL45VariableAllocationTexture& _parent;
const uint16_t _sourceMip;
const uint16_t _targetMip;
const uint8_t _face;
const uint32_t _lines;
const uint32_t _lineOffset;
// Holds the contents to transfer to the GPU in CPU memory
std::vector<uint8_t> _buffer;
// Indicates if a transfer from backing storage to interal storage has started
bool _bufferingStarted { false };
bool _transferOnly { false };
bool _bufferingCompleted { false };
VoidLambda _transferLambda;
VoidLambda _bufferingLambda;
@ -128,6 +122,7 @@ public:
static void bufferLoop();
public:
TransferJob(const TransferJob& other) = delete;
TransferJob(const GL45VariableAllocationTexture& parent, std::function<void()> transferLambda);
TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0);
bool tryTransfer();
@ -139,7 +134,7 @@ public:
void transfer();
};
using TransferQueue = std::queue<TransferJob>;
using TransferQueue = std::queue<std::unique_ptr<TransferJob>>;
static MemoryPressureState _memoryPressureState;
protected:
static std::atomic<bool> _memoryPressureStateStale;

View file

@ -78,11 +78,18 @@ void TransferJob::stopTransferLoop() {
}
TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset)
: _parent(parent), _sourceMip(sourceMip), _targetMip(targetMip), _face(face), _lines(lines), _lineOffset(lineOffset) {
: _parent(parent) {
auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip);
GLenum format;
GLenum type;
auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat());
format = texelFormat.format;
type = texelFormat.type;
if (0 == lines) {
_bufferingLambda = [this] {
auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face);
_bufferingLambda = [=] {
auto size = mipData->getSize();
_buffer.resize(size);
memcpy(&_buffer[0], mipData->readData(), size);
@ -90,38 +97,39 @@ TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t s
};
} else {
_bufferingLambda = [this] {
auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face);
auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip);
transferDimensions.y = lines;
_bufferingLambda = [=] {
auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip);
auto mipSize = mipData->getSize();
auto bytesPerLine = (uint32_t)mipSize / dimensions.y;
auto transferSize = bytesPerLine * _lines;
auto sourceOffset = bytesPerLine * _lineOffset;
auto transferSize = bytesPerLine * lines;
auto sourceOffset = bytesPerLine * lineOffset;
_buffer.resize(transferSize);
memcpy(&_buffer[0], mipData->readData() + sourceOffset, transferSize);
_bufferingCompleted = true;
};
}
_transferLambda = [this] {
auto mipData = _parent._gpuObject.accessStoredMipFace(_sourceMip, _face);
auto dimensions = _parent._gpuObject.evalMipDimensions(_sourceMip);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), mipData->getFormat());
_parent.copyMipFaceLinesFromTexture(_targetMip, _face, dimensions, _lineOffset, texelFormat.format, texelFormat.type, &_buffer[0]);
_transferLambda = [=] {
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data());
_buffer.swap(std::vector<uint8_t>());
};
}
TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function<void()> transferLambda)
: _parent(parent), _sourceMip(0), _targetMip(0), _face(0), _lines(0), _lineOffset(0), _bufferingCompleted(true), _transferLambda(transferLambda) {
if (!_bufferThread) {
_bufferThread = std::make_shared<std::thread>([] {
TransferJob::bufferLoop();
});
}
: _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) {
}
bool TransferJob::tryTransfer() {
// Disable threaded texture transfer for now
#if 1
if (!_bufferingCompleted) {
_bufferingLambda();
_bufferingCompleted = true;
}
_transferLambda();
return true;
#else
// Are we ready to transfer
if (_bufferingCompleted) {
_transferLambda();
@ -130,6 +138,7 @@ bool TransferJob::tryTransfer() {
startBuffering();
return false;
#endif
}
void TransferJob::startBuffering() {
@ -391,7 +400,7 @@ void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& cu
if (!_pendingTransfers.empty()) {
// Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture
_currentTransferTexture = currentTexture;
if (_pendingTransfers.front().tryTransfer()) {
if (_pendingTransfers.front()->tryTransfer()) {
_pendingTransfers.pop();
_currentTransferTexture.reset();
}
@ -542,7 +551,7 @@ void GL45ResourceTexture::populateTransferQueue() {
// If the mip is less than the max transfer size, then just do it in one transfer
if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) {
// Can the mip be transferred in one go
_pendingTransfers.emplace(*this, sourceMip, targetMip, face);
_pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face));
continue;
}
@ -556,13 +565,13 @@ void GL45ResourceTexture::populateTransferQueue() {
uint32_t lineOffset = 0;
while (lineOffset < lines) {
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
_pendingTransfers.emplace(TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset));
_pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset));
lineOffset += linesToCopy;
}
}
// queue up the sampler and populated mip change for after the transfer has completed
_pendingTransfers.emplace(TransferJob(*this, [=] {
_pendingTransfers.emplace(new TransferJob(*this, [=] {
_populatedMip = sourceMip;
syncSampler();
}));

View file

@ -122,36 +122,12 @@ uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 };
Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) :
_format(format),
_sysmem(size, bytes),
_isGPULoaded(false) {
Texture::updateTextureCPUMemoryUsage(0, _sysmem.getSize());
_storage(new storage::MemoryStorage(size, bytes)) {
Texture::updateTextureCPUMemoryUsage(0, _storage->size());
}
Texture::Pixels::~Pixels() {
Texture::updateTextureCPUMemoryUsage(_sysmem.getSize(), 0);
}
Texture::Size Texture::Pixels::resize(Size pSize) {
auto prevSize = _sysmem.getSize();
auto newSize = _sysmem.resize(pSize);
Texture::updateTextureCPUMemoryUsage(prevSize, newSize);
return newSize;
}
Texture::Size Texture::Pixels::setData(const Element& format, Size size, const Byte* bytes ) {
_format = format;
auto prevSize = _sysmem.getSize();
auto newSize = _sysmem.setData(size, bytes);
Texture::updateTextureCPUMemoryUsage(prevSize, newSize);
_isGPULoaded = false;
return newSize;
}
void Texture::Pixels::notifyGPULoaded() {
_isGPULoaded = true;
auto prevSize = _sysmem.getSize();
auto newSize = _sysmem.resize(0);
Texture::updateTextureCPUMemoryUsage(prevSize, newSize);
Texture::updateTextureCPUMemoryUsage(_storage->size(), 0);
}
void Texture::Storage::assignTexture(Texture* texture) {
@ -183,14 +159,6 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa
return PixelsPointer();
}
void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const {
PixelsPointer mipFace = getMipFace(level, face);
// Free the mips
if (mipFace) {
mipFace->notifyGPULoaded();
}
}
bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const {
PixelsPointer mipFace = getMipFace(level, face);
return (mipFace && mipFace->getSize());
@ -229,7 +197,8 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s
auto faceBytes = bytes;
Size allocated = 0;
for (auto& face : mip) {
allocated += face->setData(format, sizePerFace, faceBytes);
face.reset(new Pixels(format, size, bytes));
allocated += size;
faceBytes += sizePerFace;
}
@ -242,11 +211,11 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s
bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) {
allocateMip(level);
auto mip = _mips[level];
auto& mip = _mips[level];
Size allocated = 0;
if (face < mip.size()) {
auto mipFace = mip[face];
allocated += mipFace->setData(format, size, bytes);
mip[face].reset(new Pixels(format, size, bytes));
allocated += size;
bumpStamp();
}

View file

@ -17,6 +17,8 @@
#include <QMetaType>
#include <QUrl>
#include <shared/Storage.h>
#include "Forward.h"
#include "Resource.h"
@ -224,26 +226,26 @@ public:
bool operator!=(const Usage& usage) { return (_flags != usage._flags); }
};
class Pixels {
public:
using StoragePointer = storage::StoragePointer;
Pixels() {}
Pixels(const Pixels& pixels) = default;
Pixels(const Element& format, Size size, const Byte* bytes);
Pixels(const Element& format, StoragePointer& storage) : _format(format), _storage(storage.release()) {}
~Pixels();
const Byte* readData() const { return _sysmem.readData(); }
Size getSize() const { return _sysmem.getSize(); }
Size resize(Size pSize);
Size setData(const Element& format, Size size, const Byte* bytes );
const Byte* readData() const { return _storage->data(); }
Size getSize() const { return _storage->size(); }
const Element& getFormat() const { return _format; }
void notifyGPULoaded();
protected:
Element _format;
Sysmem _sysmem;
bool _isGPULoaded;
StoragePointer _storage;
friend class Texture;
};
@ -296,10 +298,6 @@ public:
const Texture* getTexture() const { return _texture; }
friend class Texture;
// THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels
// have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then
virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const;
};
@ -481,9 +479,6 @@ public:
const Sampler& getSampler() const { return _sampler; }
Stamp getSamplerStamp() const { return _samplerStamp; }
// Only callable by the Backend
void notifyMipFaceGPULoaded(uint16 level, uint8 face = 0) const { return _storage->notifyMipFaceGPULoaded(level, face); }
void setExternalTexture(uint32 externalId, void* externalFence);
void setExternalRecycler(const ExternalRecycler& recycler);
ExternalRecycler getExternalRecycler() const;

View file

@ -72,7 +72,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) {
auto mip = texture.accessStoredMipFace(level);
if (mip) {
images.emplace_back(ktx::Image(mip->getSize(), 0, mip->readData()));
images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData()));
}
}

View file

@ -12,8 +12,16 @@
using namespace storage;
ViewStoragePointer Storage::createView(size_t viewSize, size_t offset) const {
auto selfSize = size();
if ((viewSize + offset) > selfSize) {
throw std::runtime_error("Unable to map file");
}
return ViewStoragePointer(new ViewStorage(viewSize, data() + offset));
}
MemoryStoragePointer Storage::toMemoryStorage() const {
return std::make_unique<MemoryStorage>(size(), data());
return MemoryStoragePointer(new MemoryStorage(size(), data()));
}
FileStoragePointer Storage::toFileStorage(const QString& filename) const {
@ -25,14 +33,6 @@ MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) {
memcpy(_data.data(), data, size);
}
const uint8_t* MemoryStorage::data() const {
return _data.data();
}
size_t MemoryStorage::size() const {
return _data.size();
}
FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) {
QFile file(filename);
if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) {
@ -52,7 +52,7 @@ FileStoragePointer FileStorage::create(const QString& filename, size_t size, con
}
}
file.close();
return std::make_unique<FileStorage>(filename);
return FileStoragePointer(new FileStorage(filename));
}
FileStorage::FileStorage(const QString& filename) : _file(filename) {
@ -75,12 +75,3 @@ FileStorage::~FileStorage() {
_file.close();
}
}
const uint8_t* FileStorage::data() const {
return _mapped;
}
size_t FileStorage::size() const {
return _file.size();
}

View file

@ -23,13 +23,15 @@ namespace storage {
using MemoryStoragePointer = std::unique_ptr<MemoryStorage>;
class FileStorage;
using FileStoragePointer = std::unique_ptr<FileStorage>;
class ViewStorage;
using ViewStoragePointer = std::unique_ptr<ViewStorage>;
class Storage {
public:
virtual ~Storage() {}
virtual const uint8_t* data() const = 0;
virtual size_t size() const = 0;
ViewStoragePointer createView(size_t size, size_t offset = 0) const;
FileStoragePointer toFileStorage(const QString& filename) const;
MemoryStoragePointer toMemoryStorage() const;
};
@ -37,8 +39,8 @@ namespace storage {
class MemoryStorage : public Storage {
public:
MemoryStorage(size_t size, const uint8_t* data);
const uint8_t* data() const override;
size_t size() const override;
const uint8_t* data() const override { return _data.data(); }
size_t size() const override { return _data.size(); }
private:
std::vector<uint8_t> _data;
};
@ -52,13 +54,22 @@ namespace storage {
FileStorage(const FileStorage& other) = delete;
FileStorage& operator=(const FileStorage& other) = delete;
const uint8_t* data() const override;
size_t size() const override;
const uint8_t* data() const override { return _mapped; }
size_t size() const override { return _file.size(); }
private:
QFile _file;
uint8_t* _mapped { nullptr };
};
class ViewStorage : public Storage {
public:
ViewStorage(size_t size, const uint8_t* data) : _size(size), _data(data) {}
const uint8_t* data() const override { return _data; }
size_t size() const override { return _size; }
private:
const size_t _size;
const uint8_t* _data;
};
}