Commit 8394ef03 authored by Laszlo Agocs's avatar Laszlo Agocs

Add full source subrect support in texture uploads

...for non-compressed textures.
parent 87520e2c
......@@ -198,9 +198,9 @@ void Window::customRender()
QRhiTextureUploadDescription::Layer::MipLevel image(d.compressedData2[0]);
// positions and sizes must be multiples of 4 here (for BC1)
image.destinationTopLeft = QPoint(16, 32);
// the image is smaller than the subresource size (128x64 vs
// the image is smaller than the subresource size (224x64 vs
// 256x256) so the size must be specified manually
image.compressedPixelSize = QSize(224, 64);
image.sourceSize = QSize(224, 64);
layer.mipImages.append(image);
desc.layers.append(layer);
u->uploadTexture(d.tex, desc);
......
......@@ -249,8 +249,24 @@ void Window::customRender()
}
// Exercise simple, full texture copy.
if (d.testStage == 5)
if (d.testStage == 4)
u->copyTexture(d.newTex, d.tex);
// Now again upload customImage but this time only a part of it.
if (d.testStage == 5) {
QRhiTextureUploadDescription desc;
QRhiTextureUploadDescription::Layer layer;
QRhiTextureUploadDescription::Layer::MipLevel mipDesc;
mipDesc.image = d.customImage;
mipDesc.destinationTopLeft = QPoint(10, 120);
mipDesc.sourceSize = QSize(50, 40);
mipDesc.sourceTopLeft = QPoint(20, 10);
layer.mipImages.append(mipDesc);
desc.layers.append(layer);
u->uploadTexture(d.newTex, desc);
}
}
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
......
......@@ -267,10 +267,25 @@ struct Q_RHI_EXPORT QRhiTextureUploadDescription
// either a QImage or compressed data (not both)
MipLevel(const QImage &image_) : image(image_) { }
MipLevel(const QByteArray &compressedData_) : compressedData(compressedData_) { }
QImage image;
QByteArray compressedData;
QPoint destinationTopLeft;
QSize compressedPixelSize; // empty = entire subresource; ignored for non-compressed as the QImage size is used instead
// Empty = entire subresource. For uncompressed this then means the
// size of the source image must match the subresource. When
// non-empty, this size is used.
//
// Works for compressed as well, but the first compressed upload
// must always match the subresource size (and sourceSize can be
// left unset) due to gfx api limitations with some backends.
QSize sourceSize;
// This is only supported for uncompressed. Setting sourceSize or
// sourceTopLeft may trigger a QImage copy internally (depending on
// the format and the gfx api).
QPoint sourceTopLeft;
};
Layer() { }
......
......@@ -743,27 +743,46 @@ void QRhiD3D11::commitResourceUpdates(QRhiResourceUpdateBatch *resourceUpdates)
for (int level = 0, levelCount = layerDesc.mipImages.count(); level != levelCount; ++level) {
const QRhiTextureUploadDescription::Layer::MipLevel mipDesc(layerDesc.mipImages[level]);
UINT subres = D3D11CalcSubresource(level, layer, texD->mipLevelCount);
const int x = mipDesc.destinationTopLeft.x();
const int y = mipDesc.destinationTopLeft.y();
const int dx = mipDesc.destinationTopLeft.x();
const int dy = mipDesc.destinationTopLeft.y();
D3D11_BOX box;
box.front = 0;
// back, right, bottom are exclusive
box.back = 1;
if (!mipDesc.image.isNull()) {
box.left = x;
box.top = y;
box.right = x + mipDesc.image.width();
box.bottom = y + mipDesc.image.height();
context->UpdateSubresource(texD->tex, subres, &box,
mipDesc.image.constBits(), mipDesc.image.bytesPerLine(), 0);
QImage img = mipDesc.image;
int w = img.width();
int h = img.height();
int bpl = img.bytesPerLine();
const uchar *p = img.constBits();
if (!mipDesc.sourceSize.isEmpty() || !mipDesc.sourceTopLeft.isNull()) {
const int sx = mipDesc.sourceTopLeft.x();
const int sy = mipDesc.sourceTopLeft.y();
if (!mipDesc.sourceSize.isEmpty()) {
w = mipDesc.sourceSize.width();
h = mipDesc.sourceSize.height();
}
if (img.depth() == 32) {
p = img.constBits() + sy * img.bytesPerLine() + sx * 4;
} else {
img = img.copy(sx, sy, w, h);
bpl = img.bytesPerLine();
p = img.constBits();
}
}
box.left = dx;
box.top = dy;
box.right = dx + w;
box.bottom = dy + h;
context->UpdateSubresource(texD->tex, subres, &box, p, bpl, 0);
} else if (!mipDesc.compressedData.isEmpty() && isCompressedFormat(texD->m_format)) {
int w, h;
if (mipDesc.compressedPixelSize.isEmpty()) {
if (mipDesc.sourceSize.isEmpty()) {
w = qFloor(float(qMax(1, texD->m_pixelSize.width() >> level)));
h = qFloor(float(qMax(1, texD->m_pixelSize.height() >> level)));
} else {
w = mipDesc.compressedPixelSize.width();
h = mipDesc.compressedPixelSize.height();
w = mipDesc.sourceSize.width();
h = mipDesc.sourceSize.height();
}
quint32 bpl = 0;
QSize blockDim;
......@@ -771,10 +790,10 @@ void QRhiD3D11::commitResourceUpdates(QRhiResourceUpdateBatch *resourceUpdates)
// Everything must be a multiple of the block width and
// height, so e.g. a mip level of size 2x2 will be 4x4 when it
// comes to the actual data.
box.left = aligned(x, blockDim.width());
box.top = aligned(y, blockDim.height());
box.right = aligned(x + w, blockDim.width());
box.bottom = aligned(y + h, blockDim.height());
box.left = aligned(dx, blockDim.width());
box.top = aligned(dy, blockDim.height());
box.right = aligned(dx + w, blockDim.width());
box.bottom = aligned(dy + h, blockDim.height());
context->UpdateSubresource(texD->tex, subres, &box,
mipDesc.compressedData.constData(), bpl, 0);
}
......
......@@ -580,20 +580,20 @@ void QRhiGles2::commitResourceUpdates(QRhiResourceUpdateBatch *resourceUpdates)
f->glBindTexture(targetBase + layer, texD->texture);
for (int level = 0, levelCount = layerDesc.mipImages.count(); level != levelCount; ++level) {
const QRhiTextureUploadDescription::Layer::MipLevel mipDesc(layerDesc.mipImages[level]);
const int x = mipDesc.destinationTopLeft.x();
const int y = mipDesc.destinationTopLeft.y();
const int dx = mipDesc.destinationTopLeft.x();
const int dy = mipDesc.destinationTopLeft.y();
if (isCompressed && !mipDesc.compressedData.isEmpty()) {
int w, h;
if (mipDesc.compressedPixelSize.isEmpty()) {
if (mipDesc.sourceSize.isEmpty()) {
w = qFloor(float(qMax(1, texD->m_pixelSize.width() >> level)));
h = qFloor(float(qMax(1, texD->m_pixelSize.height() >> level)));
} else {
w = mipDesc.compressedPixelSize.width();
h = mipDesc.compressedPixelSize.height();
w = mipDesc.sourceSize.width();
h = mipDesc.sourceSize.height();
}
if (texD->specified) {
f->glCompressedTexSubImage2D(targetBase + layer, level,
x, y, w, h,
dx, dy, w, h,
texD->glintformat,
mipDesc.compressedData.size(), mipDesc.compressedData.constData());
} else {
......@@ -603,9 +603,23 @@ void QRhiGles2::commitResourceUpdates(QRhiResourceUpdateBatch *resourceUpdates)
mipDesc.compressedData.size(), mipDesc.compressedData.constData());
}
} else {
QImage img = mipDesc.image;
int w = img.width();
int h = img.height();
const uchar *p = img.constBits();
if (!mipDesc.sourceSize.isEmpty() || !mipDesc.sourceTopLeft.isNull()) {
const int sx = mipDesc.sourceTopLeft.x();
const int sy = mipDesc.sourceTopLeft.y();
if (!mipDesc.sourceSize.isEmpty()) {
w = mipDesc.sourceSize.width();
h = mipDesc.sourceSize.height();
}
img = img.copy(sx, sy, w, h);
p = img.constBits();
}
f->glTexSubImage2D(targetBase + layer, level,
x, y, mipDesc.image.width(), mipDesc.image.height(),
texD->glformat, texD->gltype, mipDesc.image.constBits());
dx, dy, w, h,
texD->glformat, texD->gltype, p);
}
}
}
......
......@@ -1986,6 +1986,7 @@ void QRhiVulkan::commitResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qWarning("Failed to map image data: %d", err);
continue;
}
QVector<QImage> tempImages; // yes, we rely heavily on implicit sharing in QImage
for (int layer = 0, layerCount = u.desc.layers.count(); layer != layerCount; ++layer) {
const QRhiTextureUploadDescription::Layer &layerDesc(u.desc.layers[layer]);
for (int level = 0, levelCount = layerDesc.mipImages.count(); level != levelCount; ++level) {
......@@ -2001,16 +2002,37 @@ void QRhiVulkan::commitResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
copyInfo.imageSubresource.layerCount = 1;
copyInfo.imageExtent.depth = 1;
const int x = mipDesc.destinationTopLeft.x();
const int y = mipDesc.destinationTopLeft.y();
const int dx = mipDesc.destinationTopLeft.x();
const int dy = mipDesc.destinationTopLeft.y();
if (!mipDesc.image.isNull()) {
imageSizeBytes = mipDesc.image.sizeInBytes();
if (imageSizeBytes > 0) {
src = mipDesc.image.constBits();
copyInfo.imageOffset.x = x;
copyInfo.imageOffset.y = y;
copyInfo.imageExtent.width = mipDesc.image.width();
copyInfo.imageExtent.height = mipDesc.image.height();
QImage img = mipDesc.image;
int w = img.width();
int h = img.height();
src = img.constBits();
copyInfo.bufferRowLength = w; // this is in pixels, not bytes
if (!mipDesc.sourceSize.isEmpty() || !mipDesc.sourceTopLeft.isNull()) {
const int sx = mipDesc.sourceTopLeft.x();
const int sy = mipDesc.sourceTopLeft.y();
if (!mipDesc.sourceSize.isEmpty()) {
w = mipDesc.sourceSize.width();
h = mipDesc.sourceSize.height();
}
if (img.depth() == 32) {
src = img.constBits() + sy * img.bytesPerLine() + sx * 4;
// bufferRowLength remains set to the original image's width
} else {
img = img.copy(sx, sy, w, h);
src = img.constBits();
copyInfo.bufferRowLength = w;
tempImages.append(img); // keep the new, temporary image alive until the vkCmdCopy
}
}
copyInfo.imageOffset.x = dx;
copyInfo.imageOffset.y = dy;
copyInfo.imageExtent.width = w;
copyInfo.imageExtent.height = h;
copyInfos.append(copyInfo);
}
} else {
......@@ -2020,22 +2042,22 @@ void QRhiVulkan::commitResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
const int subresw = qFloor(float(qMax(1, utexD->m_pixelSize.width() >> level)));
const int subresh = qFloor(float(qMax(1, utexD->m_pixelSize.height() >> level)));
int w, h;
if (mipDesc.compressedPixelSize.isEmpty()) {
if (mipDesc.sourceSize.isEmpty()) {
w = subresw;
h = subresh;
} else {
w = mipDesc.compressedPixelSize.width();
h = mipDesc.compressedPixelSize.height();
w = mipDesc.sourceSize.width();
h = mipDesc.sourceSize.height();
}
QSize blockDim;
compressedFormatInfo(utexD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
// x and y must be multiples of the block width and height
copyInfo.imageOffset.x = aligned(x, blockDim.width());
copyInfo.imageOffset.y = aligned(y, blockDim.height());
copyInfo.imageOffset.x = aligned(dx, blockDim.width());
copyInfo.imageOffset.y = aligned(dy, blockDim.height());
// width and height must be multiples of the block width and height
// or x + width and y + height must equal the subresource width and height
copyInfo.imageExtent.width = x + w == subresw ? w : aligned(w, blockDim.width());
copyInfo.imageExtent.height = y + h == subresh ? h : aligned(h, blockDim.height());
copyInfo.imageExtent.width = dx + w == subresw ? w : aligned(w, blockDim.width());
copyInfo.imageExtent.height = dy + h == subresh ? h : aligned(h, blockDim.height());
copyInfos.append(copyInfo);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment