Commit cafd62bb authored by Laszlo Agocs's avatar Laszlo Agocs
Browse files

vk: implement image copying

Color only, one subres at a time, transitions between transfer and
shader-read every time. For the common cases this is good enough,
to be explored later if it could be made smarter.
parent fe83848a
...@@ -83,7 +83,7 @@ int main(int argc, char **argv) ...@@ -83,7 +83,7 @@ int main(int argc, char **argv)
return 1; return 1;
} }
QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::ReadBack); QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
tex->build(); tex->build();
QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex }); QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex });
QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor(); QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor();
......
...@@ -96,7 +96,7 @@ int main(int argc, char **argv) ...@@ -96,7 +96,7 @@ int main(int argc, char **argv)
return 1; return 1;
} }
QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::ReadBack); QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
tex->build(); tex->build();
QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex }); QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex });
QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor(); QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor();
......
...@@ -106,7 +106,7 @@ int main(int argc, char **argv) ...@@ -106,7 +106,7 @@ int main(int argc, char **argv)
return 1; return 1;
} }
QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::ReadBack); QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
tex->build(); tex->build();
QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex }); QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex });
QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor(); QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor();
......
...@@ -77,7 +77,7 @@ void Window::customInit() ...@@ -77,7 +77,7 @@ void Window::customInit()
d.ubuf->build(); d.ubuf->build();
QImage baseImage(QLatin1String(":/qt256.png")); QImage baseImage(QLatin1String(":/qt256.png"));
d.tex = m_r->newTexture(QRhiTexture::RGBA8, baseImage.size()); d.tex = m_r->newTexture(QRhiTexture::RGBA8, baseImage.size(), QRhiTexture::UsedAsTransferSource);
d.tex->build(); d.tex->build();
// As an alternative to what some of the other examples do, prepare an // As an alternative to what some of the other examples do, prepare an
...@@ -232,9 +232,9 @@ void Window::customRender() ...@@ -232,9 +232,9 @@ void Window::customRender()
// Copy the left-half of tex to the right-half of newTex, while // Copy the left-half of tex to the right-half of newTex, while
// leaving the left-half of newTex blue. Keep a 20 pixel gap at // leaving the left-half of newTex blue. Keep a 20 pixel gap at
// the top. // the top.
desc.sourceTopLeft = QPointF(0, 20); desc.sourceTopLeft = QPoint(0, 20);
desc.pixelSize = QSizeF(sz.width() / 2.0f, sz.height() - 20); desc.pixelSize = QSize(sz.width() / 2, sz.height() - 20);
desc.destinationTopLeft = QPointF(sz.width() / 2.0f, 20); desc.destinationTopLeft = QPoint(sz.width() / 2, 20);
u->copyTexture(d.newTex, d.tex, desc); u->copyTexture(d.newTex, d.tex, desc);
......
...@@ -288,15 +288,15 @@ Q_DECLARE_TYPEINFO(QRhiTextureUploadDescription, Q_MOVABLE_TYPE); ...@@ -288,15 +288,15 @@ Q_DECLARE_TYPEINFO(QRhiTextureUploadDescription, Q_MOVABLE_TYPE);
struct Q_RHI_EXPORT QRhiTextureCopyDescription struct Q_RHI_EXPORT QRhiTextureCopyDescription
{ {
QSizeF pixelSize; // empty = entire texture QSize pixelSize; // empty = entire texture
int sourceLayer = 0; int sourceLayer = 0;
int sourceLevel = 0; int sourceLevel = 0;
QPointF sourceTopLeft; QPoint sourceTopLeft;
int destinationLayer = 0; int destinationLayer = 0;
int destinationLevel = 0; int destinationLevel = 0;
QPointF destinationTopLeft; QPoint destinationTopLeft;
}; };
Q_DECLARE_TYPEINFO(QRhiTextureCopyDescription, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiTextureCopyDescription, Q_MOVABLE_TYPE);
...@@ -411,7 +411,7 @@ public: ...@@ -411,7 +411,7 @@ public:
CubeMap = 1 << 2, CubeMap = 1 << 2,
MipMapped = 1 << 3, MipMapped = 1 << 3,
sRGB = 1 << 4, sRGB = 1 << 4,
ReadBack = 1 << 5 UsedAsTransferSource = 1 << 5
}; };
Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_FLAGS(Flags, Flag)
......
...@@ -776,7 +776,7 @@ void QRhiD3D11::commitResourceUpdates(QRhiResourceUpdateBatch *resourceUpdates) ...@@ -776,7 +776,7 @@ void QRhiD3D11::commitResourceUpdates(QRhiResourceUpdateBatch *resourceUpdates)
UINT dstSubRes = D3D11CalcSubresource(u.desc.destinationLevel, u.desc.destinationLayer, dstD->mipLevelCount); UINT dstSubRes = D3D11CalcSubresource(u.desc.destinationLevel, u.desc.destinationLayer, dstD->mipLevelCount);
const float dx = u.desc.destinationTopLeft.x(); const float dx = u.desc.destinationTopLeft.x();
const float dy = u.desc.destinationTopLeft.y(); const float dy = u.desc.destinationTopLeft.y();
const QSizeF size = u.desc.pixelSize.isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize; const QSize size = u.desc.pixelSize.isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize;
D3D11_BOX srcBox; D3D11_BOX srcBox;
srcBox.left = u.desc.sourceTopLeft.x(); srcBox.left = u.desc.sourceTopLeft.x();
srcBox.top = u.desc.sourceTopLeft.y(); srcBox.top = u.desc.sourceTopLeft.y();
......
...@@ -1500,6 +1500,7 @@ bool QRhiVulkan::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription & ...@@ -1500,6 +1500,7 @@ bool QRhiVulkan::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &
copyDesc.imageExtent.depth = 1; copyDesc.imageExtent.depth = 1;
if (texD) { if (texD) {
Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedAsTransferSource));
// assume the image was written // assume the image was written
imageBarrier(cb, texD, imageBarrier(cb, texD,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
...@@ -2065,6 +2066,73 @@ void QRhiVulkan::commitResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate ...@@ -2065,6 +2066,73 @@ void QRhiVulkan::commitResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
} }
for (const QRhiResourceUpdateBatchPrivate::TextureCopy &u : ud->textureCopies) {
Q_ASSERT(u.src && u.dst);
QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
VkImageCopy region;
memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = u.desc.sourceLevel;
region.srcSubresource.baseArrayLayer = u.desc.sourceLayer;
region.srcSubresource.layerCount = 1;
region.srcOffset.x = u.desc.sourceTopLeft.x();
region.srcOffset.y = u.desc.sourceTopLeft.y();
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = u.desc.destinationLevel;
region.dstSubresource.baseArrayLayer = u.desc.destinationLayer;
region.dstSubresource.layerCount = 1;
region.dstOffset.x = u.desc.destinationTopLeft.x();
region.dstOffset.y = u.desc.destinationTopLeft.y();
const QSize size = u.desc.pixelSize.isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize;
region.extent.width = size.width();
region.extent.height = size.height();
region.extent.depth = 1;
if (srcD->layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
Q_ASSERT(srcD->m_flags.testFlag(QRhiTexture::UsedAsTransferSource));
imageBarrier(cb, srcD,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
if (dstD->layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
if (dstD->layout == VK_IMAGE_LAYOUT_PREINITIALIZED) {
imageBarrier(cb, dstD,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
} else {
imageBarrier(cb, dstD,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
}
df->vkCmdCopyImage(QRHI_RES(QVkCommandBuffer, cb)->cb,
srcD->image, srcD->layout,
dstD->image, dstD->layout,
1, &region);
imageBarrier(cb, srcD,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
imageBarrier(cb, dstD,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
for (const QRhiResourceUpdateBatchPrivate::TexturePrepare &u : ud->texturePrepares) { for (const QRhiResourceUpdateBatchPrivate::TexturePrepare &u : ud->texturePrepares) {
if (u.flags.testFlag(QRhiResourceUpdateBatch::TextureRead)) { if (u.flags.testFlag(QRhiResourceUpdateBatch::TextureRead)) {
QVkTexture *utexD = QRHI_RES(QVkTexture, u.tex); QVkTexture *utexD = QRHI_RES(QVkTexture, u.tex);
...@@ -3087,7 +3155,7 @@ bool QVkTexture::build() ...@@ -3087,7 +3155,7 @@ bool QVkTexture::build()
else else
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
} }
if (m_flags.testFlag(QRhiTexture::ReadBack)) if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo; VmaAllocationCreateInfo allocInfo;
...@@ -3294,6 +3362,7 @@ bool QVkTextureRenderTarget::build() ...@@ -3294,6 +3362,7 @@ bool QVkTextureRenderTarget::build()
d.colorAttCount = m_desc.colorAttachments.count(); d.colorAttCount = m_desc.colorAttachments.count();
for (int i = 0; i < d.colorAttCount; ++i) { for (int i = 0; i < d.colorAttCount; ++i) {
QVkTexture *texD = QRHI_RES(QVkTexture, m_desc.colorAttachments[i].texture); QVkTexture *texD = QRHI_RES(QVkTexture, m_desc.colorAttachments[i].texture);
Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
VkImageView view = texD->imageView; VkImageView view = texD->imageView;
if (texD->flags().testFlag(QRhiTexture::CubeMap)) { if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
const int face = m_desc.colorAttachments[i].layer; const int face = m_desc.colorAttachments[i].layer;
......
vk, gl, mtl: texcopy gl, mtl: texcopy
mtl: tex upload with pos mtl: tex upload with pos
mtl: rhi without a window, offscreen frame mtl: rhi without a window, offscreen frame
mtl: readback (tex, backbuffer) mtl: readback (tex, backbuffer)
...@@ -46,6 +46,7 @@ indirect draw? ...@@ -46,6 +46,7 @@ indirect draw?
vk: subpasses? vk: subpasses?
more tex: 3d, array? more tex: 3d, array?
vk compressed tex: could it consume a complete ktx without any memcpys? vk compressed tex: could it consume a complete ktx without any memcpys?
multi mip/layer copy? (fewer barriers...)
multi-buffer (region) readback? multi-buffer (region) readback?
depth readback depth readback
copy image depth copy image depth
...@@ -55,6 +56,7 @@ dxc for d3d as an alternative to fxc? ...@@ -55,6 +56,7 @@ dxc for d3d as an alternative to fxc?
hlsl -> dxc -> spirv -> spirv-cross hmmm... hlsl -> dxc -> spirv -> spirv-cross hmmm...
+++ done +++ done
vk: texcopy
move cb api into QRhiCommandBuffer move cb api into QRhiCommandBuffer
d3d: texcopy d3d: texcopy
copyimage (color, with rect?, no resolve or transforms here) copyimage (color, with rect?, no resolve or transforms here)
......
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