diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-05-19 19:30:53 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-05-31 17:16:57 +0200 |
commit | 51c22a1f51e2f91289c938be7754f957c9bfa47e (patch) | |
tree | 58eea1c4aeb83f4ee2ede516f321624ab5558181 /src/gui/rhi/qrhid3d11.cpp | |
parent | 5eab5d62530f351307a01ca57af964e5c506079c (diff) |
rhi: Add support for 3D textures
Supported on OpenGL (and ES) 3.0+ and everywhere else.
Can also be a render target, targeting a single slice at a time.
Can be mipmapped, cannot be multisample.
Reading back a given slice from a 3D texture is left as a future
exercise, for now it is documented to be not supported.
Upload is going to be limited to one slice in one upload entry,
just like we specify one face or one miplevel for cubemap and
mipmapped textures.
This also involves some welcome hardening of how texture subresources
are described internally: as we no longer can count on a layer index
between 0..5 (as is the case with cubemaps), simply arrays with
MAX_LAYER==6 are no longer sufficient. Switch to sufficiently dynamic
data structures where applicable.
On Vulkan rendering to a slice needs Vulkan 1.1 (and 1.1 enabled on the
VkInstance).
Task-number: QTBUG-89703
Change-Id: Ide6c20124ec9201d94ffc339dd479cd1ece777b0
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/gui/rhi/qrhid3d11.cpp')
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 178 |
1 files changed, 125 insertions, 53 deletions
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 8d15f2622f..d16332e5b9 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -546,6 +546,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::RenderBufferImport: return false; + case QRhi::ThreeDimensionalTextures: + return true; + case QRhi::RenderTo3DTextureSlice: + return true; default: Q_UNREACHABLE(); return false; @@ -633,10 +637,11 @@ QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, con return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint); } -QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, const QSize &pixelSize, +QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, + const QSize &pixelSize, int depth, int sampleCount, QRhiTexture::Flags flags) { - return new QD3D11Texture(this, format, pixelSize, sampleCount, flags); + return new QD3D11Texture(this, format, pixelSize, depth, sampleCount, flags); } QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -1351,17 +1356,18 @@ QRhi::FrameOpResult QRhiD3D11::finish() void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc) { - UINT subres = D3D11CalcSubresource(UINT(level), UINT(layer), texD->mipLevelCount); - const QPoint dp = subresDesc.destinationTopLeft(); + const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional); + UINT subres = D3D11CalcSubresource(UINT(level), is3D ? 0u : UINT(layer), texD->mipLevelCount); D3D11_BOX box; - box.front = 0; + box.front = is3D ? UINT(layer) : 0u; // back, right, bottom are exclusive - box.back = 1; + box.back = box.front + 1; QD3D11CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; - cmd.args.updateSubRes.dst = texD->tex; + cmd.args.updateSubRes.dst = texD->textureResource(); cmd.args.updateSubRes.dstSubRes = subres; + const QPoint dp = subresDesc.destinationTopLeft(); if (!subresDesc.image().isNull()) { QImage img = subresDesc.image(); QSize size = img.size(); @@ -1488,6 +1494,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.copySubRes.dstSubRes = 0; cmd.args.copySubRes.dstX = 0; cmd.args.copySubRes.dstY = 0; + cmd.args.copySubRes.dstZ = 0; cmd.args.copySubRes.src = bufD->buffer; cmd.args.copySubRes.srcSubRes = 0; cmd.args.copySubRes.hasSrcBox = true; @@ -1508,8 +1515,8 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]); if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst); - for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { - for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) { + for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) { for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) enqueueSubresUpload(texD, cbD, layer, level, subresDesc); } @@ -1518,8 +1525,10 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate Q_ASSERT(u.src && u.dst); QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src); QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst); - UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), UINT(u.desc.sourceLayer()), srcD->mipLevelCount); - UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), UINT(u.desc.destinationLayer()), dstD->mipLevelCount); + const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional); + const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional); + UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), srcIs3D ? 0u : UINT(u.desc.sourceLayer()), srcD->mipLevelCount); + UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), dstIs3D ? 0u : UINT(u.desc.destinationLayer()), dstD->mipLevelCount); const QPoint dp = u.desc.destinationTopLeft(); const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize); const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize(); @@ -1527,18 +1536,19 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate D3D11_BOX srcBox; srcBox.left = UINT(sp.x()); srcBox.top = UINT(sp.y()); - srcBox.front = 0; + srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u; // back, right, bottom are exclusive srcBox.right = srcBox.left + UINT(copySize.width()); srcBox.bottom = srcBox.top + UINT(copySize.height()); - srcBox.back = 1; + srcBox.back = srcBox.front + 1; QD3D11CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; - cmd.args.copySubRes.dst = dstD->tex; + cmd.args.copySubRes.dst = dstD->textureResource(); cmd.args.copySubRes.dstSubRes = dstSubRes; cmd.args.copySubRes.dstX = UINT(dp.x()); cmd.args.copySubRes.dstY = UINT(dp.y()); - cmd.args.copySubRes.src = srcD->tex; + cmd.args.copySubRes.dstZ = dstIs3D ? UINT(u.desc.destinationLayer()) : 0u; + cmd.args.copySubRes.src = srcD->textureResource(); cmd.args.copySubRes.srcSubRes = srcSubRes; cmd.args.copySubRes.hasSrcBox = true; cmd.args.copySubRes.srcBox = srcBox; @@ -1560,7 +1570,13 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate qWarning("Multisample texture cannot be read back"); continue; } - src = texD->tex; + // No support for reading back 3D, not because it is not + // possible technically, but we need to draw the line somewhere. + if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)) { + qWarning("3D texture cannot be read back"); + continue; + } + src = texD->textureResource(); dxgiFormat = texD->dxgiFormat; pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize); format = texD->m_format; @@ -1616,6 +1632,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.copySubRes.dstSubRes = 0; cmd.args.copySubRes.dstX = 0; cmd.args.copySubRes.dstY = 0; + cmd.args.copySubRes.dstZ = 0; cmd.args.copySubRes.src = src; cmd.args.copySubRes.srcSubRes = subres; cmd.args.copySubRes.hasSrcBox = false; @@ -1791,12 +1808,12 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource Q_ASSERT(srcTexD || srcRbD); QD3D11CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes; - cmd.args.resolveSubRes.dst = dstTexD->tex; + cmd.args.resolveSubRes.dst = dstTexD->textureResource(); cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(UINT(colorAtt.resolveLevel()), UINT(colorAtt.resolveLayer()), dstTexD->mipLevelCount); if (srcTexD) { - cmd.args.resolveSubRes.src = srcTexD->tex; + cmd.args.resolveSubRes.src = srcTexD->textureResource(); if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) { qWarning("Resolve source (%d) and destination (%d) formats do not match", int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat)); @@ -2582,7 +2599,7 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * break; case QD3D11CommandBuffer::Command::CopySubRes: context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes, - cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, 0, + cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, cmd.args.copySubRes.dstZ, cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes, cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr); break; @@ -2885,11 +2902,11 @@ QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat; } -QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, +QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, sampleCount, flags) + : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) { - for (int i = 0; i < QRhi::MAX_LEVELS; ++i) + for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) perLevelViews[i] = nullptr; } @@ -2900,7 +2917,7 @@ QD3D11Texture::~QD3D11Texture() void QD3D11Texture::destroy() { - if (!tex) + if (!tex && !tex3D) return; if (srv) { @@ -2908,17 +2925,22 @@ void QD3D11Texture::destroy() srv = nullptr; } - for (int i = 0; i < QRhi::MAX_LEVELS; ++i) { + for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) { if (perLevelViews[i]) { perLevelViews[i]->Release(); perLevelViews[i] = nullptr; } } - if (owns) - tex->Release(); + if (owns) { + if (tex) + tex->Release(); + if (tex3D) + tex3D->Release(); + } tex = nullptr; + tex3D = nullptr; QRHI_RES_RHI(QRhiD3D11); QRHI_PROF; @@ -2962,12 +2984,13 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format) bool QD3D11Texture::prepareCreate(QSize *adjustedSize) { - if (tex) + if (tex || tex3D) destroy(); const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); + const bool is3D = m_flags.testFlag(ThreeDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); QRHI_RES_RHI(QRhiD3D11); @@ -2979,6 +3002,10 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize) qWarning("Cubemap texture cannot be multisample"); return false; } + if (is3D) { + qWarning("3D texture cannot be multisample"); + return false; + } if (hasMipMaps) { qWarning("Multisample texture cannot have mipmaps"); return false; @@ -2988,6 +3015,15 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize) qWarning("Depth texture cannot have mipmaps"); return false; } + if (isCube && is3D) { + qWarning("Texture cannot be both cube and 3D"); + return false; + } + m_depth = qMax(1, m_depth); + if (m_depth > 1 && !is3D) { + qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); + return false; + } if (adjustedSize) *adjustedSize = size; @@ -3000,6 +3036,7 @@ bool QD3D11Texture::finishCreate() QRHI_RES_RHI(QRhiD3D11); const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); + const bool is3D = m_flags.testFlag(ThreeDimensional); D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; memset(&srvDesc, 0, sizeof(srvDesc)); @@ -3010,13 +3047,16 @@ bool QD3D11Texture::finishCreate() } else { if (sampleDesc.Count > 1) { srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; + } else if (is3D) { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + srvDesc.Texture3D.MipLevels = mipLevelCount; } else { srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = mipLevelCount; } } - HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv); + HRESULT hr = rhiD->dev->CreateShaderResourceView(textureResource(), &srvDesc, &srv); if (FAILED(hr)) { qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr))); return false; @@ -3034,6 +3074,7 @@ bool QD3D11Texture::create() const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); + const bool is3D = m_flags.testFlag(ThreeDimensional); uint bindFlags = D3D11_BIND_SHADER_RESOURCE; uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; @@ -3054,31 +3095,51 @@ bool QD3D11Texture::create() if (m_flags.testFlag(UsedWithLoadStore)) bindFlags |= D3D11_BIND_UNORDERED_ACCESS; - D3D11_TEXTURE2D_DESC desc; - memset(&desc, 0, sizeof(desc)); - desc.Width = UINT(size.width()); - desc.Height = UINT(size.height()); - desc.MipLevels = mipLevelCount; - desc.ArraySize = isCube ? 6 : 1; - desc.Format = dxgiFormat; - desc.SampleDesc = sampleDesc; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = bindFlags; - desc.MiscFlags = miscFlags; - QRHI_RES_RHI(QRhiD3D11); - HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); - if (FAILED(hr)) { - qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr))); - return false; + if (!is3D) { + D3D11_TEXTURE2D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = UINT(size.width()); + desc.Height = UINT(size.height()); + desc.MipLevels = mipLevelCount; + desc.ArraySize = isCube ? 6 : 1; + desc.Format = dxgiFormat; + desc.SampleDesc = sampleDesc; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = bindFlags; + desc.MiscFlags = miscFlags; + + HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex); + if (FAILED(hr)) { + qWarning("Failed to create 2D texture: %s", qPrintable(comErrorMessage(hr))); + return false; + } + if (!m_objectName.isEmpty()) + tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData()); + } else { + D3D11_TEXTURE3D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = UINT(size.width()); + desc.Height = UINT(size.height()); + desc.Depth = UINT(m_depth); + desc.MipLevels = mipLevelCount; + desc.Format = dxgiFormat; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = bindFlags; + desc.MiscFlags = miscFlags; + + HRESULT hr = rhiD->dev->CreateTexture3D(&desc, nullptr, &tex3D); + if (FAILED(hr)) { + qWarning("Failed to create 3D texture: %s", qPrintable(comErrorMessage(hr))); + return false; + } + if (!m_objectName.isEmpty()) + tex3D->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData()); } if (!finishCreate()) return false; - if (!m_objectName.isEmpty()) - tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData()); - QRHI_PROF; QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count))); @@ -3089,14 +3150,16 @@ bool QD3D11Texture::create() bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src) { - ID3D11Texture2D *srcTex = reinterpret_cast<ID3D11Texture2D *>(src.object); - if (srcTex == nullptr) + if (!src.object) return false; if (!prepareCreate()) return false; - tex = srcTex; + if (m_flags.testFlag(ThreeDimensional)) + tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object); + else + tex = reinterpret_cast<ID3D11Texture2D *>(src.object); if (!finishCreate()) return false; @@ -3112,7 +3175,7 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src) QRhiTexture::NativeTexture QD3D11Texture::nativeTexture() { - return {quint64(tex), 0}; + return { quint64(textureResource()), 0 }; } ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) @@ -3121,6 +3184,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) return perLevelViews[level]; const bool isCube = m_flags.testFlag(CubeMap); + const bool is3D = m_flags.testFlag(ThreeDimensional); D3D11_UNORDERED_ACCESS_VIEW_DESC desc; memset(&desc, 0, sizeof(desc)); desc.Format = dxgiFormat; @@ -3129,6 +3193,9 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) desc.Texture2DArray.MipSlice = UINT(level); desc.Texture2DArray.FirstArraySlice = 0; desc.Texture2DArray.ArraySize = 6; + } else if (is3D) { + desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; + desc.Texture3D.MipSlice = UINT(level); } else { desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; desc.Texture2D.MipSlice = UINT(level); @@ -3136,7 +3203,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) QRHI_RES_RHI(QRhiD3D11); ID3D11UnorderedAccessView *uav = nullptr; - HRESULT hr = rhiD->dev->CreateUnorderedAccessView(tex, &desc, &uav); + HRESULT hr = rhiD->dev->CreateUnorderedAccessView(textureResource(), &desc, &uav); if (FAILED(hr)) { qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr))); return nullptr; @@ -3404,6 +3471,11 @@ bool QD3D11TextureRenderTarget::create() rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); rtvDesc.Texture2DArray.ArraySize = 1; + } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level()); + rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer()); + rtvDesc.Texture3D.WSize = 1; } else { if (texD->sampleDesc.Count > 1) { rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; @@ -3412,7 +3484,7 @@ bool QD3D11TextureRenderTarget::create() rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level()); } } - HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[attIndex]); + HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->textureResource(), &rtvDesc, &rtv[attIndex]); if (FAILED(hr)) { qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr))); return false; |