From 8669b0f782cf944ceb51a437bcdad95178b40d16 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs <laszlo.agocs@qt.io> Date: Wed, 5 Dec 2018 10:40:30 +0100 Subject: [PATCH] d3d, vk, gl: Add resolve color only --- .../rhi/msaarenderbuffer/msaarenderbuffer.cpp | 6 +- src/rhi/qrhi.cpp | 17 ++ src/rhi/qrhi.h | 102 ++++++---- src/rhi/qrhi_p.h | 12 ++ src/rhi/qrhid3d11.cpp | 73 ++++++- src/rhi/qrhid3d11_p.h | 13 +- src/rhi/qrhigles2.cpp | 49 +++++ src/rhi/qrhigles2_p.h | 12 +- src/rhi/qrhimetal.mm | 5 + src/rhi/qrhimetal_p.h | 1 + src/rhi/qrhivulkan.cpp | 192 +++++++++++++----- src/rhi/qrhivulkan_p.h | 6 + todo.txt | 14 +- 13 files changed, 392 insertions(+), 110 deletions(-) diff --git a/examples/rhi/msaarenderbuffer/msaarenderbuffer.cpp b/examples/rhi/msaarenderbuffer/msaarenderbuffer.cpp index cecfe2c..6c3c982 100644 --- a/examples/rhi/msaarenderbuffer/msaarenderbuffer.cpp +++ b/examples/rhi/msaarenderbuffer/msaarenderbuffer.cpp @@ -235,7 +235,11 @@ void Window::customRender() cb->setViewport({ 0, 0, float(d.rb->pixelSize().width()), float(d.rb->pixelSize().height()) }); cb->setVertexInput(0, { { d.vbuf, sizeof(vertexData) } }); cb->draw(3); - cb->endPass(); + + // add the resolve (msaa renderbuffer -> non-msaa texture) + u = m_r->nextResourceUpdateBatch(); + u->resolveRenderBuffer(d.tex, d.rb); + cb->endPass(u); // onscreen (quad) const QSize outputSizeInPixels = m_sc->effectivePixelSize(); diff --git a/src/rhi/qrhi.cpp b/src/rhi/qrhi.cpp index b26d710..c62ca59 100644 --- a/src/rhi/qrhi.cpp +++ b/src/rhi/qrhi.cpp @@ -464,6 +464,21 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src) d->textureCopies.append({ dst, src, QRhiTextureCopyDescription() }); } +void QRhiResourceUpdateBatch::resolveTexture(QRhiTexture *dst, const QRhiTextureResolveDescription &desc) +{ + d->textureResolves.append({ dst, desc }); +} + +void QRhiResourceUpdateBatch::resolveTexture(QRhiTexture *dst, QRhiTexture *src) +{ + d->textureResolves.append({ dst, src }); +} + +void QRhiResourceUpdateBatch::resolveRenderBuffer(QRhiTexture *dst, QRhiRenderBuffer *src) +{ + d->textureResolves.append({ dst, src }); +} + void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) { d->textureReadbacks.append({ rb, result }); @@ -511,6 +526,7 @@ void QRhiResourceUpdateBatchPrivate::free() staticBufferUploads.clear(); textureUploads.clear(); textureCopies.clear(); + textureResolves.clear(); textureReadbacks.clear(); texturePrepares.clear(); @@ -524,6 +540,7 @@ void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other staticBufferUploads += other->staticBufferUploads; textureUploads += other->textureUploads; textureCopies += other->textureCopies; + textureResolves += other->textureResolves; textureReadbacks += other->textureReadbacks; texturePrepares += other->texturePrepares; } diff --git a/src/rhi/qrhi.h b/src/rhi/qrhi.h index 971af34..d1bbf81 100644 --- a/src/rhi/qrhi.h +++ b/src/rhi/qrhi.h @@ -322,6 +322,23 @@ struct Q_RHI_EXPORT QRhiTextureCopyDescription Q_DECLARE_TYPEINFO(QRhiTextureCopyDescription, Q_MOVABLE_TYPE); +struct Q_RHI_EXPORT QRhiTextureResolveDescription +{ + QRhiTextureResolveDescription() { } + QRhiTextureResolveDescription(QRhiTexture *src) : sourceTexture(src) { } + QRhiTextureResolveDescription(QRhiRenderBuffer *src) : sourceRenderBuffer(src) { } + + // source is either a multisample texture or a multisample renderbuffer + QRhiTexture *sourceTexture = nullptr; + int sourceLayer = 0; + QRhiRenderBuffer *sourceRenderBuffer = nullptr; + + int destinationLayer = 0; + int destinationLevel = 0; +}; + +Q_DECLARE_TYPEINFO(QRhiTextureResolveDescription, Q_MOVABLE_TYPE); + struct Q_RHI_EXPORT QRhiReadbackDescription { QRhiReadbackDescription() { } // source is the back buffer of the swapchain of the current frame (if the swapchain supports readback) @@ -385,45 +402,6 @@ protected: Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiBuffer::UsageFlags) -class Q_RHI_EXPORT QRhiRenderBuffer : public QRhiResource -{ -public: - enum Type { - DepthStencil, - Color - }; - - enum Flag { - ToBeUsedWithSwapChainOnly = 1 << 0 // use implicit winsys buffers, don't create anything (GL) - }; - Q_DECLARE_FLAGS(Flags, Flag) - - Type type() const { return m_type; } - void setType(Type t) { m_type = t; } - - QSize pixelSize() const { return m_pixelSize; } - void setPixelSize(const QSize &sz) { m_pixelSize = sz; } - - int sampleCount() const { return m_sampleCount; } - void setSampleCount(int s) { m_sampleCount = s; } - - Flags flags() const { return m_flags; } - void setFlags(Flags h) { m_flags = h; } - - virtual bool build() = 0; - -protected: - QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_, - int sampleCount_, Flags flags_); - Type m_type; - QSize m_pixelSize; - int m_sampleCount; - Flags m_flags; - void *m_reserved; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags) - class Q_RHI_EXPORT QRhiTexture : public QRhiResource { public: @@ -433,7 +411,7 @@ public: CubeMap = 1 << 2, MipMapped = 1 << 3, sRGB = 1 << 4, - UsedAsTransferSource = 1 << 5 // will (also) be used as the source of a readback or copy + UsedAsTransferSource = 1 << 5 // will (also) be used as the source of a readback or copy or resolve }; Q_DECLARE_FLAGS(Flags, Flag) @@ -552,6 +530,47 @@ protected: void *m_reserved; }; +class Q_RHI_EXPORT QRhiRenderBuffer : public QRhiResource +{ +public: + enum Type { + DepthStencil, + Color + }; + + enum Flag { + ToBeUsedWithSwapChainOnly = 1 << 0 // use implicit winsys buffers, don't create anything (GL) + }; + Q_DECLARE_FLAGS(Flags, Flag) + + Type type() const { return m_type; } + void setType(Type t) { m_type = t; } + + QSize pixelSize() const { return m_pixelSize; } + void setPixelSize(const QSize &sz) { m_pixelSize = sz; } + + int sampleCount() const { return m_sampleCount; } + void setSampleCount(int s) { m_sampleCount = s; } + + Flags flags() const { return m_flags; } + void setFlags(Flags h) { m_flags = h; } + + virtual bool build() = 0; + + virtual QRhiTexture::Format backingFormat() const = 0; + +protected: + QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_, + int sampleCount_, Flags flags_); + Type m_type; + QSize m_pixelSize; + int m_sampleCount; + Flags m_flags; + void *m_reserved; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags) + class Q_RHI_EXPORT QRhiRenderPassDescriptor : public QRhiResource { protected: @@ -1006,6 +1025,9 @@ public: void uploadTexture(QRhiTexture *tex, const QImage &image); // shortcut void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc); void copyTexture(QRhiTexture *dst, QRhiTexture *src); // shortcut + void resolveTexture(QRhiTexture *dst, const QRhiTextureResolveDescription &desc); + void resolveTexture(QRhiTexture *dst, QRhiTexture *src); // shortcut + void resolveRenderBuffer(QRhiTexture *dst, QRhiRenderBuffer *src); // shortcut void readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result); // This is not normally needed, textures that have an upload or are used diff --git a/src/rhi/qrhi_p.h b/src/rhi/qrhi_p.h index 092b1ab..8177709 100644 --- a/src/rhi/qrhi_p.h +++ b/src/rhi/qrhi_p.h @@ -184,6 +184,16 @@ struct QRhiResourceUpdateBatchPrivate QRhiTextureCopyDescription desc; }; + struct TextureResolve { + TextureResolve() { } + TextureResolve(QRhiTexture *dst_, const QRhiTextureResolveDescription &desc_) + : dst(dst_), desc(desc_) + { } + + QRhiTexture *dst = nullptr; + QRhiTextureResolveDescription desc; + }; + struct TextureRead { TextureRead() { } TextureRead(const QRhiReadbackDescription &rb_, QRhiReadbackResult *result_) @@ -208,6 +218,7 @@ struct QRhiResourceUpdateBatchPrivate QVector<StaticBufferUpload> staticBufferUploads; QVector<TextureUpload> textureUploads; QVector<TextureCopy> textureCopies; + QVector<TextureResolve> textureResolves; QVector<TextureRead> textureReadbacks; QVector<TexturePrepare> texturePrepares; @@ -225,6 +236,7 @@ Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABL Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureUpload, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureCopy, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureResolve, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureRead, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TexturePrepare, Q_MOVABLE_TYPE); diff --git a/src/rhi/qrhid3d11.cpp b/src/rhi/qrhid3d11.cpp index ef8be17..25fa7a6 100644 --- a/src/rhi/qrhid3d11.cpp +++ b/src/rhi/qrhid3d11.cpp @@ -821,6 +821,52 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cbD->commands.append(cmd); } + for (const QRhiResourceUpdateBatchPrivate::TextureResolve &u : ud->textureResolves) { + Q_ASSERT(u.dst); + Q_ASSERT(u.desc.sourceTexture || u.desc.sourceRenderBuffer); + QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, u.dst); + if (dstTexD->sampleDesc.Count > 1) { + qWarning("Cannot resolve into a multisample texture"); + continue; + } + QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, u.desc.sourceTexture); + QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, u.desc.sourceRenderBuffer); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes; + cmd.args.resolveSubRes.dst = dstTexD->tex; + cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(u.desc.destinationLevel, + u.desc.destinationLayer, + dstTexD->mipLevelCount); + if (srcTexD) { + cmd.args.resolveSubRes.src = srcTexD->tex; + if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) { + qWarning("Resolve source and destination formats do not match"); + continue; + } + if (srcTexD->sampleDesc.Count <= 1) { + qWarning("Cannot resolve a non-multisample texture"); + continue; + } + if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + } else { + cmd.args.resolveSubRes.src = srcRbD->tex; + if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) { + qWarning("Resolve source and destination formats do not match"); + continue; + } + if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + } + cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, u.desc.sourceLayer, 1); + cmd.args.resolveSubRes.format = dstTexD->dxgiFormat; + cbD->commands.append(cmd); + } + for (const QRhiResourceUpdateBatchPrivate::TextureRead &u : ud->textureReadbacks) { ActiveReadback aRb; aRb.desc = u.rb; @@ -840,7 +886,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate continue; } src = texD->tex; - dxgiFormat = toD3DTextureFormat(texD->m_format, texD->m_flags); + dxgiFormat = texD->dxgiFormat; pixelSize = texD->m_pixelSize; if (u.rb.level > 0) { pixelSize.setWidth(qFloor(float(qMax(1, pixelSize.width() >> u.rb.level)))); @@ -1288,6 +1334,11 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD) cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes, cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr); break; + case QD3D11CommandBuffer::Command::ResolveSubRes: + context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes, + cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes, + cmd.args.resolveSubRes.format); + break; default: break; } @@ -1382,7 +1433,6 @@ bool QD3D11RenderBuffer::build() QRHI_RES_RHI(QRhiD3D11); sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); - static const DXGI_FORMAT dsFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; D3D11_TEXTURE2D_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -1394,7 +1444,8 @@ bool QD3D11RenderBuffer::build() desc.Usage = D3D11_USAGE_DEFAULT; if (m_type == Color) { - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.Format = dxgiFormat; desc.BindFlags = D3D11_BIND_RENDER_TARGET; HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); if (FAILED(hr)) { @@ -1403,8 +1454,7 @@ bool QD3D11RenderBuffer::build() } D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; memset(&rtvDesc, 0, sizeof(rtvDesc)); - rtvDesc.Format = desc.Format; - rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS + rtvDesc.Format = dxgiFormat; rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D; hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv); if (FAILED(hr)) { @@ -1413,7 +1463,8 @@ bool QD3D11RenderBuffer::build() } return true; } else if (m_type == DepthStencil) { - desc.Format = dsFormat; + dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + desc.Format = dxgiFormat; desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); if (FAILED(hr)) { @@ -1422,7 +1473,7 @@ bool QD3D11RenderBuffer::build() } D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; memset(&dsvDesc, 0, sizeof(dsvDesc)); - dsvDesc.Format = dsFormat; + dsvDesc.Format = dxgiFormat; dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv); @@ -1436,6 +1487,11 @@ bool QD3D11RenderBuffer::build() return false; } +QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int sampleCount, Flags flags) : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) @@ -1493,6 +1549,7 @@ bool QD3D11Texture::build() const bool isCube = m_flags.testFlag(CubeMap); const bool hasMipMaps = m_flags.testFlag(MipMapped); + dxgiFormat = toD3DTextureFormat(m_format, m_flags); mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1; sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); if (sampleDesc.Count > 1) { @@ -1520,7 +1577,7 @@ bool QD3D11Texture::build() desc.Height = size.height(); desc.MipLevels = mipLevelCount; desc.ArraySize = isCube ? 6 : 1;; - desc.Format = toD3DTextureFormat(m_format, m_flags); + desc.Format = dxgiFormat; desc.SampleDesc = sampleDesc; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = bindFlags; diff --git a/src/rhi/qrhid3d11_p.h b/src/rhi/qrhid3d11_p.h index c476678..a6db830 100644 --- a/src/rhi/qrhid3d11_p.h +++ b/src/rhi/qrhid3d11_p.h @@ -70,10 +70,12 @@ struct QD3D11RenderBuffer : public QRhiRenderBuffer int sampleCount, QRhiRenderBuffer::Flags flags); void release() override; bool build() override; + QRhiTexture::Format backingFormat() const override; ID3D11Texture2D *tex = nullptr; ID3D11DepthStencilView *dsv = nullptr; ID3D11RenderTargetView *rtv = nullptr; + DXGI_FORMAT dxgiFormat; DXGI_SAMPLE_DESC sampleDesc; friend class QRhiD3D11; }; @@ -87,6 +89,7 @@ struct QD3D11Texture : public QRhiTexture ID3D11Texture2D *tex = nullptr; ID3D11ShaderResourceView *srv = nullptr; + DXGI_FORMAT dxgiFormat; uint mipLevelCount = 0; DXGI_SAMPLE_DESC sampleDesc; uint generation = 0; @@ -244,7 +247,8 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer Draw, DrawIndexed, UpdateSubRes, - CopySubRes + CopySubRes, + ResolveSubRes }; enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 }; Cmd cmd; @@ -324,6 +328,13 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer bool hasSrcBox; D3D11_BOX srcBox; } copySubRes; + struct { + ID3D11Resource *dst; + UINT dstSubRes; + ID3D11Resource *src; + UINT srcSubRes; + DXGI_FORMAT format; + } resolveSubRes; } args; }; diff --git a/src/rhi/qrhigles2.cpp b/src/rhi/qrhigles2.cpp index 6d67cc6..279acb2 100644 --- a/src/rhi/qrhigles2.cpp +++ b/src/rhi/qrhigles2.cpp @@ -702,6 +702,30 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cbD->commands.append(cmd); } + for (const QRhiResourceUpdateBatchPrivate::TextureResolve &u : ud->textureResolves) { + Q_ASSERT(u.dst); + Q_ASSERT(u.desc.sourceTexture || u.desc.sourceRenderBuffer); + if (u.desc.sourceTexture) { + qWarning("Multisample textures not supported, skipping resolve"); + continue; + } + QGles2Texture *dstTexD = QRHI_RES(QGles2Texture, u.dst); + QGles2RenderBuffer *srcRbD = QRHI_RES(QGles2RenderBuffer, u.desc.sourceRenderBuffer); + if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer; + cmd.args.blitFromRb.renderbuffer = srcRbD->renderbuffer; + cmd.args.blitFromRb.w = srcRbD->m_pixelSize.width(); + cmd.args.blitFromRb.h = srcRbD->m_pixelSize.height(); + cmd.args.blitFromRb.dst = dstTexD; + cmd.args.blitFromRb.dstLayer = u.desc.destinationLayer; + cmd.args.blitFromRb.dstLevel = u.desc.destinationLevel; + cbD->commands.append(cmd); + } + for (const QRhiResourceUpdateBatchPrivate::TextureRead &u : ud->textureReadbacks) { QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::ReadPixels; @@ -1149,6 +1173,26 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) cmd.args.compressedSubImage.glintformat, cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data); break; + case QGles2CommandBuffer::Command::BlitFromRenderbuffer: + { + GLuint fbo[2]; + f->glGenFramebuffers(2, fbo); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]); + f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer); + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]); + QGles2Texture *texD = cmd.args.blitFromRb.dst; + const GLenum targetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; + const GLenum target = targetBase + cmd.args.blitFromRb.dstLayer; + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, + texD->texture, cmd.args.blitFromRb.dstLevel); + f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, + 0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, + GL_COLOR_BUFFER_BIT, + GL_LINEAR); + f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); + } + break; default: break; } @@ -1523,6 +1567,11 @@ bool QGles2RenderBuffer::build() return true; } +QRhiTexture::Format QGles2RenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int sampleCount, Flags flags) : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) diff --git a/src/rhi/qrhigles2_p.h b/src/rhi/qrhigles2_p.h index 86817e5..dd9a2b0 100644 --- a/src/rhi/qrhigles2_p.h +++ b/src/rhi/qrhigles2_p.h @@ -78,6 +78,7 @@ struct QGles2RenderBuffer : public QRhiRenderBuffer int sampleCount, QRhiRenderBuffer::Flags flags); void release() override; bool build() override; + QRhiTexture::Format backingFormat() const override; GLuint renderbuffer = 0; int samples; @@ -240,7 +241,8 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer ReadPixels, SubImage, CompressedImage, - CompressedSubImage + CompressedSubImage, + BlitFromRenderbuffer }; Cmd cmd; union { @@ -351,6 +353,14 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer int size; const void *data; // must come from retainData() } compressedSubImage; + struct { + GLuint renderbuffer; + int w; + int h; + QGles2Texture *dst; + int dstLayer; + int dstLevel; + } blitFromRb; } args; }; diff --git a/src/rhi/qrhimetal.mm b/src/rhi/qrhimetal.mm index bcf415e..9c0bb90 100644 --- a/src/rhi/qrhimetal.mm +++ b/src/rhi/qrhimetal.mm @@ -951,6 +951,11 @@ bool QMetalRenderBuffer::build() return true; } +QRhiTexture::Format QMetalRenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int sampleCount, Flags flags) : QRhiTexture(rhi, format, pixelSize, sampleCount, flags), diff --git a/src/rhi/qrhimetal_p.h b/src/rhi/qrhimetal_p.h index ce89fa6..edd5702 100644 --- a/src/rhi/qrhimetal_p.h +++ b/src/rhi/qrhimetal_p.h @@ -75,6 +75,7 @@ struct QMetalRenderBuffer : public QRhiRenderBuffer ~QMetalRenderBuffer(); void release() override; bool build() override; + QRhiTexture::Format backingFormat() const override; QMetalRenderBufferData *d; uint generation = 0; diff --git a/src/rhi/qrhivulkan.cpp b/src/rhi/qrhivulkan.cpp index 3950ca8..7d9aed5 100644 --- a/src/rhi/qrhivulkan.cpp +++ b/src/rhi/qrhivulkan.cpp @@ -796,8 +796,7 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp, QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture); QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer); Q_ASSERT(texD || rbD); - const VkFormat vkformat = texD ? toVkTextureFormat(texD->m_format, texD->m_flags) - : toVkTextureFormat(rbD->backingTexture->m_format, rbD->backingTexture->m_flags); + const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat; VkAttachmentDescription attDesc; memset(&attDesc, 0, sizeof(attDesc)); @@ -817,8 +816,8 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp, const bool hasDepthStencil = depthStencilBuffer || depthTexture; if (hasDepthStencil) { - const VkFormat dsFormat = depthTexture ? toVkTextureFormat(depthTexture->format(), depthTexture->flags()) - : optimalDepthStencilFormat(); + const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat + : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat; const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples; VkAttachmentDescription attDesc; @@ -1798,6 +1797,42 @@ void QRhiVulkan::imageBarrier(QRhiCommandBuffer *cb, QRhiTexture *tex, texD->layout = newLayout; } +void QRhiVulkan::prepareForTransferDest(QRhiCommandBuffer *cb, QVkTexture *texD) +{ + if (texD->layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + if (texD->layout == VK_IMAGE_LAYOUT_PREINITIALIZED) { + imageBarrier(cb, texD, + 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, texD, + 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); + } + } +} + +void QRhiVulkan::finishTransferDest(QRhiCommandBuffer *cb, QVkTexture *texD) +{ + imageBarrier(cb, texD, + 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); +} + +void QRhiVulkan::prepareForTransferSrc(QRhiCommandBuffer *cb, QVkTexture *texD) +{ + if (texD->layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedAsTransferSource)); + imageBarrier(cb, texD, + 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); + } +} + void QRhiVulkan::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); @@ -2004,19 +2039,7 @@ void QRhiVulkan::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdat vmaUnmapMemory(toVmaAllocator(allocator), a); vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize); - if (utexD->layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - if (utexD->layout == VK_IMAGE_LAYOUT_PREINITIALIZED) { - imageBarrier(cb, u.tex, - 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, u.tex, - 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); - } - } + prepareForTransferDest(cb, utexD); df->vkCmdCopyBufferToImage(cbD->cb, utexD->stagingBuffers[currentFrameSlot], utexD->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -2034,10 +2057,7 @@ void QRhiVulkan::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdat releaseQueue.append(e); } - imageBarrier(cb, u.tex, - 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); + finishTransferDest(cb, utexD); } for (const QRhiResourceUpdateBatchPrivate::TextureCopy &u : ud->textureCopies) { @@ -2069,27 +2089,8 @@ void QRhiVulkan::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdat 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); - } - } + prepareForTransferSrc(cb, srcD); + prepareForTransferDest(cb, dstD); df->vkCmdCopyImage(QRHI_RES(QVkCommandBuffer, cb)->cb, srcD->image, srcD->layout, @@ -2101,10 +2102,76 @@ void QRhiVulkan::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdat 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); + finishTransferDest(cb, dstD); + } + + for (const QRhiResourceUpdateBatchPrivate::TextureResolve &u : ud->textureResolves) { + Q_ASSERT(u.dst); + Q_ASSERT(u.desc.sourceTexture || u.desc.sourceRenderBuffer); + QVkTexture *dstTexD = QRHI_RES(QVkTexture, u.dst); + if (dstTexD->samples > VK_SAMPLE_COUNT_1_BIT) { + qWarning("Cannot resolve into a multisample texture"); + continue; + } + + QVkTexture *srcTexD = QRHI_RES(QVkTexture, u.desc.sourceTexture); + QVkRenderBuffer *srcRbD = QRHI_RES(QVkRenderBuffer, u.desc.sourceRenderBuffer); + VkImage srcImage; + VkImageLayout srcLayout; + VkImageResolve region; + memset(®ion, 0, sizeof(region)); + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.baseArrayLayer = u.desc.sourceLayer; + region.srcSubresource.layerCount = 1; + 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.extent.depth = 1; + + if (srcTexD) { + if (srcTexD->vkformat != dstTexD->vkformat) { + qWarning("Resolve source and destination formats do not match"); + continue; + } + if (srcTexD->samples == VK_SAMPLE_COUNT_1_BIT) { + qWarning("Cannot resolve a non-multisample texture"); + continue; + } + if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + + region.extent.width = srcTexD->m_pixelSize.width(); + region.extent.height = srcTexD->m_pixelSize.height(); + + prepareForTransferSrc(cb, srcTexD); + srcImage = srcTexD->image; + srcLayout = srcTexD->layout; + } else { + if (srcRbD->vkformat != dstTexD->vkformat) { + qWarning("Resolve source and destination formats do not match"); + continue; + } + if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) { + qWarning("Resolve source and destination sizes do not match"); + continue; + } + + region.extent.width = srcRbD->m_pixelSize.width(); + region.extent.height = srcRbD->m_pixelSize.height(); + + prepareForTransferSrc(cb, srcRbD->backingTexture); + srcImage = srcRbD->backingTexture->image; + srcLayout = srcRbD->backingTexture->layout; + } + + prepareForTransferDest(cb, dstTexD); + + df->vkCmdResolveImage(cbD->cb, srcImage, srcLayout, dstTexD->image, dstTexD->layout, 1, ®ion); + + finishTransferDest(cb, dstTexD); } for (const QRhiResourceUpdateBatchPrivate::TextureRead &u : ud->textureReadbacks) { @@ -3134,8 +3201,10 @@ void QVkRenderBuffer::release() QRHI_RES_RHI(QRhiVulkan); rhiD->releaseQueue.append(e); - if (backingTexture) + if (backingTexture) { + Q_ASSERT(backingTexture->lastActiveFrameSlot == -1); backingTexture->release(); + } } bool QVkRenderBuffer::build() @@ -3150,15 +3219,19 @@ bool QVkRenderBuffer::build() case QRhiRenderBuffer::Color: { if (!backingTexture) { - backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(QRhiTexture::RGBA8, m_pixelSize, - m_sampleCount, QRhiTexture::RenderTarget)); + backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(QRhiTexture::RGBA8, + m_pixelSize, + m_sampleCount, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); } if (!backingTexture->build()) return false; + vkformat = backingTexture->vkformat; } break; case QRhiRenderBuffer::DepthStencil: - if (!rhiD->createTransientImage(rhiD->optimalDepthStencilFormat(), + vkformat = rhiD->optimalDepthStencilFormat(); + if (!rhiD->createTransientImage(vkformat, m_pixelSize, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, @@ -3180,6 +3253,11 @@ bool QVkRenderBuffer::build() return true; } +QRhiTexture::Format QVkRenderBuffer::backingFormat() const +{ + return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; +} + QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int sampleCount, Flags flags) : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) @@ -3225,7 +3303,7 @@ bool QVkTexture::build() release(); QRHI_RES_RHI(QRhiVulkan); - VkFormat vkformat = toVkTextureFormat(m_format, m_flags); + vkformat = toVkTextureFormat(m_format, m_flags); VkFormatProperties props; rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props); const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); @@ -3241,6 +3319,16 @@ bool QVkTexture::build() mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1; samples = rhiD->effectiveSampleCount(m_sampleCount); + if (samples > VK_SAMPLE_COUNT_1_BIT) { + if (isCube) { + qWarning("Cubemap texture cannot be multisample"); + return false; + } + if (hasMipMaps) { + qWarning("Multisample texture cannot have mipmaps"); + return false; + } + } VkImageCreateInfo imageInfo; memset(&imageInfo, 0, sizeof(imageInfo)); @@ -3482,7 +3570,7 @@ bool QVkTextureRenderTarget::build() viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = texD->image; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = toVkTextureFormat(texD->format(), texD->flags()); + viewInfo.format = texD->vkformat; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; diff --git a/src/rhi/qrhivulkan_p.h b/src/rhi/qrhivulkan_p.h index 40d25e9..9aaecb6 100644 --- a/src/rhi/qrhivulkan_p.h +++ b/src/rhi/qrhivulkan_p.h @@ -85,12 +85,14 @@ struct QVkRenderBuffer : public QRhiRenderBuffer ~QVkRenderBuffer(); void release() override; bool build() override; + QRhiTexture::Format backingFormat() const override; VkDeviceMemory memory = VK_NULL_HANDLE; VkImage image = VK_NULL_HANDLE; VkImageView imageView = VK_NULL_HANDLE; VkSampleCountFlagBits samples; QVkTexture *backingTexture = nullptr; + VkFormat vkformat; int lastActiveFrameSlot = -1; friend class QRhiVulkan; }; @@ -108,6 +110,7 @@ struct QVkTexture : public QRhiTexture VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; VkImageLayout layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + VkFormat vkformat; uint mipLevelCount = 0; VkSampleCountFlagBits samples; int lastActiveFrameSlot = -1; @@ -411,6 +414,9 @@ public: QRhi::FrameOpResult endNonWrapperFrame(QRhiSwapChain *swapChain); void prepareNewFrame(QRhiCommandBuffer *cb); void prepareFrameEnd(); + void prepareForTransferDest(QRhiCommandBuffer *cb, QVkTexture *texD); + void finishTransferDest(QRhiCommandBuffer *cb, QVkTexture *texD); + void prepareForTransferSrc(QRhiCommandBuffer *cb, QVkTexture *texD); void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD); void activateTextureRenderTarget(QRhiCommandBuffer *cb, QRhiTextureRenderTarget *rt); diff --git a/todo.txt b/todo.txt index 1cc0656..80e092f 100644 --- a/todo.txt +++ b/todo.txt @@ -16,12 +16,9 @@ test cubemap test cubemap face as target face cubemap readback? (test vk/d3d, impl for gl/mtl) -d3d: resolveimage (color) -vk: resolveimage (color) -gl: resolveimage (color) - gl: tex formats (texture, readback) gl: srgb +gl: readback and resolve could be made more optimal by taking a rt as source mipmap generation? @@ -63,15 +60,18 @@ more tex: 3d, array? vk compressed tex: could it consume a complete ktx without any memcpys? multi mip/layer copy? (fewer barriers...) multi-buffer (region) readback? -depth readback -copy image depth -depth resolve +depth readback? +copy image depth? +depth resolve? shadertools: dxc for d3d as an alternative to fxc? hlsl -> dxc -> spirv -> spirv-cross hmmm... +++ done +d3d: resolveimage (color) +vk: resolveimage (color) +gl: resolveimage (color) vk: color renderbuffer d3d: color renderbuffer gl: color renderbuffer -- GitLab