Add incremental transfers for large mips

This commit is contained in:
Brad Davis 2017-02-09 10:20:24 -08:00
parent 7fb7aa87eb
commit 1238edd0d7
3 changed files with 82 additions and 31 deletions

View file

@ -38,7 +38,8 @@ public:
protected:
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
void generateMips() const override;
void copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const;
void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const;
void copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const;
virtual void syncSampler() const;
};

View file

@ -120,32 +120,36 @@ void GL45Texture::generateMips() const {
(void)CHECK_GL_ERROR();
}
void GL45Texture::copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const {
void GL45Texture::copyMipFaceLinesFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lineOffset, uint32_t lines, size_t dataOffset) const {
const auto& texture = _gpuObject;
if (!texture.isStoredMipFaceAvailable(sourceMip)) {
return;
}
size_t maxFace = GLTexture::getFaceCount(_target);
for (uint8_t face = 0; face < maxFace; ++face) {
auto size = texture.evalMipDimensions(sourceMip);
auto mipData = texture.accessStoredMipFace(sourceMip, face);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat());
if (GL_TEXTURE_2D == _target) {
glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData());
} else if (GL_TEXTURE_CUBE_MAP == _target) {
// DSA ARB does not work on AMD, so use EXT
// unless EXT is not available on the driver
if (glTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData());
} else {
glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mipData->readData());
}
auto mipDimensions = texture.evalMipDimensions(sourceMip);
glm::uvec3 size = { mipDimensions.x, lines, mipDimensions.z };
auto mipData = texture.accessStoredMipFace(sourceMip, face);
auto sourcePointer = mipData->readData() + dataOffset;
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat());
if (GL_TEXTURE_2D == _target) {
glTextureSubImage2D(_id, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer);
} else if (GL_TEXTURE_CUBE_MAP == _target) {
// DSA ARB does not work on AMD, so use EXT
// unless EXT is not available on the driver
if (glTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, targetMip, 0, lineOffset, size.x, size.y, texelFormat.format, texelFormat.type, sourcePointer);
} else {
Q_ASSERT(false);
glTextureSubImage3D(_id, targetMip, 0, lineOffset, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, sourcePointer);
}
(void)CHECK_GL_ERROR();
} else {
Q_ASSERT(false);
}
(void)CHECK_GL_ERROR();
}
void GL45Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const {
auto size = _gpuObject.evalMipDimensions(sourceMip);
copyMipFaceLinesFromTexture(sourceMip, targetMip, face, 0, size.y, 0);
}
void GL45Texture::syncSampler() const {
@ -221,7 +225,10 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr<GLBacke
auto mipLevels = _gpuObject.evalNumMips();
for (uint16_t sourceMip = 0; sourceMip < mipLevels; ++sourceMip) {
uint16_t targetMip = sourceMip;
copyMipFromTexture(sourceMip, targetMip);
size_t maxFace = GLTexture::getFaceCount(_target);
for (uint8_t face = 0; face < maxFace; ++face) {
copyMipFaceFromTexture(sourceMip, targetMip, face);
}
}
if (texture.isAutogenerateMips()) {
generateMips();

View file

@ -359,9 +359,12 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) {
void GL45ResourceTexture::copyMipsFromTexture() {
auto mipLevels = _gpuObject.evalNumMips();
size_t maxFace = GLTexture::getFaceCount(_target);
for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) {
uint16_t targetMip = sourceMip - _allocatedMip;
copyMipFromTexture(sourceMip, targetMip);
for (uint8_t face = 0; face < maxFace; ++face) {
copyMipFaceFromTexture(sourceMip, targetMip, face);
}
}
}
@ -448,17 +451,57 @@ void GL45ResourceTexture::populateTransferQueue() {
return;
}
for (int16_t mip = _populatedMip - 1; mip >= _allocatedMip; --mip) {
// FIXME break down the transfers into chunks so that no single transfer is
// consuming more than X bandwidth
_pendingTransfers.push([mip, this] {
Q_ASSERT(mip >= _allocatedMip);
// FIXME modify the copy mechanism to be incremental
copyMipFromTexture(mip, mip - _allocatedMip);
_populatedMip = mip;
static const uvec3 MAX_TRANSFER_DIMENSIONS { 512, 512, 1 };
static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4;
const uint8_t maxFace = GLTexture::getFaceCount(_target);
uint16_t sourceMip = _populatedMip;
do {
--sourceMip;
auto targetMip = sourceMip - _allocatedMip;
auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip);
for (uint8_t face = 0; face < maxFace; ++face) {
if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) {
continue;
}
// 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.push([=] {
Q_ASSERT(sourceMip >= _allocatedMip);
// FIXME modify the copy mechanism to be incremental
copyMipFaceFromTexture(sourceMip, targetMip, face);
});
continue;
}
// break down the transfers into chunks so that no single transfer is
// consuming more than X bandwidth
auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face);
const auto lines = mipDimensions.y;
auto bytesPerLine = (uint32_t)mipData->getSize() / lines;
Q_ASSERT(0 == (mipData->getSize() % lines));
auto linesPerTransfer = MAX_TRANSFER_SIZE / bytesPerLine;
size_t offset = 0;
uint32_t lineOffset = 0;
while (lineOffset < lines) {
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
uvec3 size { mipDimensions.x, linesToCopy, 1 };
_pendingTransfers.push([=] {
copyMipFaceLinesFromTexture(sourceMip, targetMip, face, lineOffset, linesToCopy, offset);
});
lineOffset += linesToCopy;
offset += (linesToCopy * bytesPerLine);
}
}
// queue up the sampler and populated mip change for after the transfer has completed
_pendingTransfers.push([=] {
_populatedMip = targetMip;
syncSampler();
});
}
} while (sourceMip != _allocatedMip);
}
// Sparsely allocated, managed size resource textures