Commit a3572d7e authored by Laszlo Agocs's avatar Laszlo Agocs

vk: Partially implement readbacks

parent e356ce28
...@@ -49,6 +49,8 @@ ...@@ -49,6 +49,8 @@
****************************************************************************/ ****************************************************************************/
#include <QGuiApplication> #include <QGuiApplication>
#include <QImage>
#include <QFileInfo>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QRhiVulkanInitParams> #include <QRhiVulkanInitParams>
...@@ -87,7 +89,7 @@ int main(int argc, char **argv) ...@@ -87,7 +89,7 @@ int main(int argc, char **argv)
return 1; return 1;
} }
QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget); QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), QRhiTexture::RenderTarget | QRhiTexture::ReadBack);
tex->build(); tex->build();
QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex }); QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex });
QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor(); QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor();
...@@ -105,6 +107,7 @@ int main(int argc, char **argv) ...@@ -105,6 +107,7 @@ int main(int argc, char **argv)
QRhiReadbackDescription rb(tex); QRhiReadbackDescription rb(tex);
QRhiReadbackResult rbResult; QRhiReadbackResult rbResult;
rbResult.completed = [frame] { qDebug(" - readback %d completed", frame); };
r->readback(cb, rb, &rbResult); r->readback(cb, rb, &rbResult);
qDebug("Submit and wait"); qDebug("Submit and wait");
...@@ -112,7 +115,16 @@ int main(int argc, char **argv) ...@@ -112,7 +115,16 @@ int main(int argc, char **argv)
// No finish() or waiting for the completed callback is needed here // No finish() or waiting for the completed callback is needed here
// since the endOffscreenFrame() implies a wait for completion. // since the endOffscreenFrame() implies a wait for completion.
qDebug() << rbResult.data.size(); if (!rbResult.data.isEmpty()) {
const uchar *p = reinterpret_cast<const uchar *>(rbResult.data.constData());
QImage image(p, 1280, 720, QImage::Format_RGBA8888);
QString fn = QString::asprintf("frame%d.png", frame);
fn = QFileInfo(fn).absoluteFilePath();
qDebug("Saving into %s", qPrintable(fn));
image.save(fn);
} else {
qWarning("Readback failed!");
}
} }
rt->releaseAndDestroy(); rt->releaseAndDestroy();
......
...@@ -425,9 +425,9 @@ QRhi::FrameOpResult QRhi::endOffscreenFrame() ...@@ -425,9 +425,9 @@ QRhi::FrameOpResult QRhi::endOffscreenFrame()
return d->endOffscreenFrame(); return d->endOffscreenFrame();
} }
void QRhi::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) bool QRhi::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
d->readback(cb, rb, result); return d->readback(cb, rb, result);
} }
QRhi::FrameOpResult QRhi::finish() QRhi::FrameOpResult QRhi::finish()
......
...@@ -396,7 +396,8 @@ public: ...@@ -396,7 +396,8 @@ public:
ChangesFrequently = 1 << 1, // hint for backend to keep staging resources around ChangesFrequently = 1 << 1, // hint for backend to keep staging resources around
CubeMap = 1 << 2, CubeMap = 1 << 2,
MipMapped = 1 << 3, MipMapped = 1 << 3,
sRGB = 1 << 4 sRGB = 1 << 4,
ReadBack = 1 << 5
}; };
Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_FLAGS(Flags, Flag)
...@@ -1004,7 +1005,7 @@ public: ...@@ -1004,7 +1005,7 @@ public:
FrameOpResult endOffscreenFrame(); FrameOpResult endOffscreenFrame();
// Cannot be inside a pass. // Cannot be inside a pass.
void readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result); bool readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result);
// Waits for any work on the graphics queue (where applicable) to complete, // Waits for any work on the graphics queue (where applicable) to complete,
// then forcibly executes all deferred operations, like completing // then forcibly executes all deferred operations, like completing
......
...@@ -85,7 +85,7 @@ public: ...@@ -85,7 +85,7 @@ public:
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) = 0; virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) = 0;
virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0; virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0;
virtual QRhi::FrameOpResult endOffscreenFrame() = 0; virtual QRhi::FrameOpResult endOffscreenFrame() = 0;
virtual void readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) = 0; virtual bool readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) = 0;
virtual QRhi::FrameOpResult finish() = 0; virtual QRhi::FrameOpResult finish() = 0;
virtual void beginPass(QRhiRenderTarget *rt, virtual void beginPass(QRhiRenderTarget *rt,
......
...@@ -508,13 +508,15 @@ QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame() ...@@ -508,13 +508,15 @@ QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame()
return QRhi::FrameOpError; return QRhi::FrameOpError;
} }
void QRhiD3D11::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) bool QRhiD3D11::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
Q_UNUSED(cb); Q_UNUSED(cb);
Q_UNUSED(rb); Q_UNUSED(rb);
Q_UNUSED(result); Q_UNUSED(result);
Q_ASSERT(inFrame && !inPass); Q_ASSERT(inFrame && !inPass);
return false;
} }
QRhi::FrameOpResult QRhiD3D11::finish() QRhi::FrameOpResult QRhiD3D11::finish()
......
...@@ -376,7 +376,7 @@ public: ...@@ -376,7 +376,7 @@ public:
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame() override;
void readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override; bool readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void beginPass(QRhiRenderTarget *rt, void beginPass(QRhiRenderTarget *rt,
......
...@@ -410,13 +410,15 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame() ...@@ -410,13 +410,15 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
return QRhi::FrameOpError; return QRhi::FrameOpError;
} }
void QRhiGles2::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) bool QRhiGles2::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
Q_UNUSED(cb); Q_UNUSED(cb);
Q_UNUSED(rb); Q_UNUSED(rb);
Q_UNUSED(result); Q_UNUSED(result);
Q_ASSERT(inFrame && !inPass); Q_ASSERT(inFrame && !inPass);
return false;
} }
QRhi::FrameOpResult QRhiGles2::finish() QRhi::FrameOpResult QRhiGles2::finish()
......
...@@ -334,7 +334,7 @@ public: ...@@ -334,7 +334,7 @@ public:
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame() override;
void readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override; bool readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void beginPass(QRhiRenderTarget *rt, void beginPass(QRhiRenderTarget *rt,
......
...@@ -568,13 +568,15 @@ QRhi::FrameOpResult QRhiMetal::endOffscreenFrame() ...@@ -568,13 +568,15 @@ QRhi::FrameOpResult QRhiMetal::endOffscreenFrame()
return QRhi::FrameOpError; return QRhi::FrameOpError;
} }
void QRhiMetal::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) bool QRhiMetal::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
Q_UNUSED(cb); Q_UNUSED(cb);
Q_UNUSED(rb); Q_UNUSED(rb);
Q_UNUSED(result); Q_UNUSED(result);
Q_ASSERT(inFrame && !inPass); Q_ASSERT(inFrame && !inPass);
return false;
} }
QRhi::FrameOpResult QRhiMetal::finish() QRhi::FrameOpResult QRhiMetal::finish()
......
...@@ -265,7 +265,7 @@ public: ...@@ -265,7 +265,7 @@ public:
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame() override;
void readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override; bool readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void beginPass(QRhiRenderTarget *rt, void beginPass(QRhiRenderTarget *rt,
......
...@@ -1382,20 +1382,68 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame() ...@@ -1382,20 +1382,68 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
void QRhiVulkan::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) bool QRhiVulkan::readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
Q_UNUSED(cb);
Q_ASSERT(inFrame && !inPass); Q_ASSERT(inFrame && !inPass);
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
// create a host visible buffer
// add a vkCmdCopyImageToBuffer
ActiveReadback aRb; ActiveReadback aRb;
aRb.activeFrameSlot = currentFrameSlot; aRb.activeFrameSlot = currentFrameSlot;
aRb.desc = rb; aRb.desc = rb;
aRb.result = result; aRb.result = result;
QVkTexture *tex = QRHI_RES(QVkTexture, aRb.desc.texture);
// ###
aRb.bufSize = tex->m_pixelSize.width() * tex->m_pixelSize.height() * 4;
// Create a host visible buffer.
VkBufferCreateInfo bufferInfo;
memset(&bufferInfo, 0, sizeof(bufferInfo));
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = aRb.bufSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VmaAllocationCreateInfo allocInfo;
memset(&allocInfo, 0, sizeof(allocInfo));
allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
VmaAllocation allocation;
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &aRb.buf, &allocation, nullptr);
if (err == VK_SUCCESS) {
aRb.bufAlloc = allocation;
} else {
qWarning("Failed to create readback buffer of size %d: %d", aRb.bufSize, err);
return false;
}
// Copy from the (optimal and not host visible) image into the buffer.
VkBufferImageCopy copyDesc;
memset(&copyDesc, 0, sizeof(copyDesc));
copyDesc.bufferOffset = 0;
copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // ### no depth for now
copyDesc.imageSubresource.mipLevel = aRb.desc.level;
copyDesc.imageSubresource.baseArrayLayer = aRb.desc.layer;
copyDesc.imageSubresource.layerCount = 1;
copyDesc.imageExtent.width = tex->m_pixelSize.width();
copyDesc.imageExtent.height = tex->m_pixelSize.height();
copyDesc.imageExtent.depth = 1;
// assume the image was written
imageBarrier(cb, tex,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
df->vkCmdCopyImageToBuffer(cbD->cb, tex->image, tex->layout, aRb.buf, 1, &copyDesc);
// behave as if the image was used for shader read
imageBarrier(cb, tex,
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);
activeReadbacks.append(aRb); activeReadbacks.append(aRb);
return true;
} }
QRhi::FrameOpResult QRhiVulkan::finish() QRhi::FrameOpResult QRhiVulkan::finish()
...@@ -2026,8 +2074,18 @@ void QRhiVulkan::finishActiveReadbacks(bool forced) ...@@ -2026,8 +2074,18 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
for (int i = activeReadbacks.count() - 1; i >= 0; --i) { for (int i = activeReadbacks.count() - 1; i >= 0; --i) {
const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]); const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]);
if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) {
// map and memcpy aRb.result->data.resize(aRb.bufSize);
// destroy temp buffer void *p = nullptr;
VmaAllocation a = toVmaAllocation(aRb.bufAlloc);
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err != VK_SUCCESS) {
qWarning("Failed to map readback buffer: %d", err);
continue;
}
memcpy(aRb.result->data.data(), p, aRb.bufSize);
vmaUnmapMemory(toVmaAllocator(allocator), a);
vmaDestroyBuffer(toVmaAllocator(allocator), aRb.buf, a);
if (aRb.result->completed) if (aRb.result->completed)
completedCallbacks.append(aRb.result->completed); completedCallbacks.append(aRb.result->completed);
...@@ -2925,6 +2983,8 @@ bool QVkTexture::build() ...@@ -2925,6 +2983,8 @@ 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))
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo; VmaAllocationCreateInfo allocInfo;
memset(&allocInfo, 0, sizeof(allocInfo)); memset(&allocInfo, 0, sizeof(allocInfo));
......
...@@ -327,7 +327,7 @@ public: ...@@ -327,7 +327,7 @@ public:
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame() override;
void readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override; bool readback(QRhiCommandBuffer *cb, const QRhiReadbackDescription &rb, QRhiReadbackResult *result) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void beginPass(QRhiRenderTarget *rt, void beginPass(QRhiRenderTarget *rt,
...@@ -473,6 +473,9 @@ public: ...@@ -473,6 +473,9 @@ public:
int activeFrameSlot = -1; int activeFrameSlot = -1;
QRhiReadbackDescription desc; QRhiReadbackDescription desc;
QRhiReadbackResult *result; QRhiReadbackResult *result;
VkBuffer buf;
QVkAlloc bufAlloc;
int bufSize;
}; };
QVector<ActiveReadback> activeReadbacks; QVector<ActiveReadback> activeReadbacks;
......
gl, mtl: compressed textures
gl, mtl: srgb (tex, swapchain buf)
rhi without a window, fully offscreen
readback api
some wait-gpu stuff for waiting for readback results inside a begin-endFrame f.ex.
vk: implement finish()
vk: readback vk: readback
mtl, d3d, gl: rhi without a window mtl, d3d, gl: rhi without a window
mtl, d3d, gl: implement finish() mtl, d3d, gl: implement finish()
mtl, d3d, gl: readback mtl, d3d, gl: readback
gl, mtl: compressed textures
gl, mtl: srgb (tex, swapchain buf)
multi window? (multi swapchain) -> trouble multi window? (multi swapchain) -> trouble
vk: rendering hangs sometimes when minimize and back on some systems? vk: rendering hangs sometimes when minimize and back on some systems?
mtl: cubemaps mtl: cubemaps
...@@ -36,6 +32,8 @@ gl: tex size stuff (npot etc.) ...@@ -36,6 +32,8 @@ gl: tex size stuff (npot etc.)
gl: tex formats gl: tex formats
more QImage->tex formats more QImage->tex formats
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-buffer (region) readback?
depth readback?
d3d, gl, mtl: cache shader sources? d3d, gl, mtl: cache shader sources?
advanced blend modes? advanced blend modes?
resize to 0 width or height fails (vk) resize to 0 width or height fails (vk)
...@@ -53,6 +51,10 @@ dxc for d3d as an alternative to fxc? ...@@ -53,6 +51,10 @@ dxc for d3d as an alternative to fxc?
hlsl -> dxc -> spirv -> spirv-cross hmmm... hlsl -> dxc -> spirv -> spirv-cross hmmm...
+++ done +++ done
vk: implement finish()
some wait-gpu stuff for waiting for readback results inside a begin-endFrame f.ex.
readback api
rhi without a window, fully offscreen
vk: compressed textures (etc2, astc, bc) vk: compressed textures (etc2, astc, bc)
vk: srgb (tex, swapchain buf) vk: srgb (tex, swapchain buf)
d3d: srgb support (texture, swapchain buf) d3d: srgb support (texture, swapchain buf)
......
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