Commit 847455ba authored by Laszlo Agocs's avatar Laszlo Agocs

vk, d3d, gl: Mipmap generation

parent 1c951c31
......@@ -55,6 +55,7 @@
#include "cube.h"
const bool MIPMAP = true;
const bool AUTOGENMIPMAP = true;
static QBakedShader getShader(const QString &name)
{
......@@ -78,6 +79,8 @@ void TexturedCubeRenderer::initResources(QRhiRenderPassDescriptor *rp)
QRhiTexture::Flags texFlags = 0;
if (MIPMAP)
texFlags |= QRhiTexture::MipMapped;
if (AUTOGENMIPMAP)
texFlags |= QRhiTexture::UsedWithGenerateMips;
m_tex = m_r->newTexture(QRhiTexture::RGBA8, QSize(m_image.width(), m_image.height()), 1, texFlags);
m_tex->build();
......@@ -189,12 +192,18 @@ void TexturedCubeRenderer::queueResourceUpdates(QRhiResourceUpdateBatch *resourc
if (MIPMAP) {
QRhiTextureUploadDescription desc;
desc.layers.append(QRhiTextureUploadDescription::Layer());
// the ghetto mipmap generator...
for (int i = 0, ie = m_r->mipLevelsForSize(m_image.size()); i != ie; ++i) {
QImage image = m_image.scaled(m_r->sizeForMipLevel(i, m_image.size()));
desc.layers[0].mipImages.push_back({ image });
if (!AUTOGENMIPMAP) {
// the ghetto mipmap generator...
for (int i = 0, ie = m_r->mipLevelsForSize(m_image.size()); i != ie; ++i) {
QImage image = m_image.scaled(m_r->sizeForMipLevel(i, m_image.size()));
desc.layers[0].mipImages.push_back({ image });
}
} else {
desc.layers[0].mipImages.push_back({ m_image });
}
resourceUpdates->uploadTexture(m_tex, desc);
if (AUTOGENMIPMAP)
resourceUpdates->generateMips(m_tex);
} else {
resourceUpdates->uploadTexture(m_tex, m_image);
}
......
......@@ -56,6 +56,8 @@
const bool IMAGE_UNDER_OFFSCREEN_RENDERING = false;
const bool UPLOAD_UNDERLAY_ON_EVERY_FRAME = false;
const bool DS_ATT = false; // have a depth-stencil attachment for the offscreen pass
const bool DEPTH_TEXTURE = false; // offscreen pass uses a depth texture (verify with renderdoc etc., ignore valid.layer about ps slot 0)
const bool MRT = false; // two textures, the second is just cleared as the shader does not write anything (valid.layer may warn but for testing that's ok)
......@@ -95,6 +97,18 @@ void TriangleOnCubeRenderer::initResources(QRhiRenderPassDescriptor *rp)
m_tex2->build();
}
if (DS_ATT) {
m_offscreenTriangle.setDepthWrite(true);
m_ds = m_r->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_tex->pixelSize());
m_ds->build();
}
if (DEPTH_TEXTURE) {
m_offscreenTriangle.setDepthWrite(true);
m_depthTex = m_r->newTexture(QRhiTexture::D32, OFFSCREEN_SIZE, 1, QRhiTexture::RenderTarget);
m_depthTex->build();
}
m_sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
m_sampler->build();
......@@ -141,12 +155,6 @@ void TriangleOnCubeRenderer::initResources(QRhiRenderPassDescriptor *rp)
m_ps->build();
if (DEPTH_TEXTURE) {
m_offscreenTriangle.setDepthWrite(true);
m_depthTex = m_r->newTexture(QRhiTexture::D32, OFFSCREEN_SIZE, 1, QRhiTexture::RenderTarget);
m_depthTex->build();
}
QRhiTextureRenderTarget::Flags rtFlags = 0;
if (IMAGE_UNDER_OFFSCREEN_RENDERING)
rtFlags |= QRhiTextureRenderTarget::PreserveColorContents;
......@@ -157,6 +165,8 @@ void TriangleOnCubeRenderer::initResources(QRhiRenderPassDescriptor *rp)
m_rt = m_r->newTextureRenderTarget(desc, rtFlags);
} else {
QRhiTextureRenderTargetDescription desc { m_tex };
if (DS_ATT)
desc.depthStencilBuffer = m_ds;
if (MRT) {
m_offscreenTriangle.setColorAttCount(2);
desc.colorAttachments.append(m_tex2);
......@@ -228,6 +238,11 @@ void TriangleOnCubeRenderer::releaseResources()
m_tex = nullptr;
}
if (m_ds) {
m_ds->releaseAndDestroy();
m_ds = nullptr;
}
if (m_ubuf) {
m_ubuf->releaseAndDestroy();
m_ubuf = nullptr;
......
......@@ -73,6 +73,7 @@ private:
bool m_vbufReady = false;
QRhiBuffer *m_ubuf = nullptr;
QRhiTexture *m_tex = nullptr;
QRhiRenderBuffer *m_ds = nullptr;
QRhiTexture *m_tex2 = nullptr;
QRhiTexture *m_depthTex = nullptr;
QRhiSampler *m_sampler = nullptr;
......
......@@ -469,6 +469,11 @@ void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb,
d->textureReadbacks.append({ rb, result });
}
void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex)
{
d->textureMipGens.append({ tex });
}
void QRhiResourceUpdateBatch::prepareTextureForUse(QRhiTexture *tex, TexturePrepareFlags flags)
{
d->texturePrepares.append({ tex, flags });
......@@ -513,6 +518,7 @@ void QRhiResourceUpdateBatchPrivate::free()
textureCopies.clear();
textureResolves.clear();
textureReadbacks.clear();
textureMipGens.clear();
texturePrepares.clear();
rhi->resUpdPoolMap.clearBit(poolIndex);
......@@ -527,6 +533,7 @@ void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other
textureCopies += other->textureCopies;
textureResolves += other->textureResolves;
textureReadbacks += other->textureReadbacks;
textureMipGens += other->textureMipGens;
texturePrepares += other->texturePrepares;
}
......
......@@ -415,7 +415,8 @@ 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 or resolve
UsedAsTransferSource = 1 << 5, // will (also) be used as the source of a readback or copy or resolve
UsedWithGenerateMips = 1 << 6
};
Q_DECLARE_FLAGS(Flags, Flag)
......@@ -918,7 +919,7 @@ public:
// Sometimes committing the updates is necessary without starting a render
// pass. Not often needed, updates should typically be passed to beginPass
// (or endPass, in case of readbacks) instead.
// (or endPass, in case of readbacks or resolves) instead.
void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates);
void beginPass(QRhiRenderTarget *rt,
......@@ -1030,6 +1031,7 @@ public:
void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription());
void resolveTexture(QRhiTexture *dst, const QRhiTextureResolveDescription &desc);
void readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result);
void generateMips(QRhiTexture *tex);
// This is not normally needed, textures that have an upload or are used
// with a TextureRenderTarget will be fine without it. May be more relevant later.
......
......@@ -204,6 +204,10 @@ struct QRhiResourceUpdateBatchPrivate
QRhiReadbackResult *result;
};
struct TextureMipGen {
QRhiTexture *tex = nullptr;
};
struct TexturePrepare {
TexturePrepare() { }
TexturePrepare(QRhiTexture *tex_, QRhiResourceUpdateBatch::TexturePrepareFlags flags_)
......@@ -220,6 +224,7 @@ struct QRhiResourceUpdateBatchPrivate
QVector<TextureCopy> textureCopies;
QVector<TextureResolve> textureResolves;
QVector<TextureRead> textureReadbacks;
QVector<TextureMipGen> textureMipGens;
QVector<TexturePrepare> texturePrepares;
QRhiResourceUpdateBatch *q = nullptr;
......@@ -238,6 +243,7 @@ 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::TextureMipGen, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TexturePrepare, Q_MOVABLE_TYPE);
template<typename T>
......
......@@ -955,6 +955,14 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
activeReadbacks.append(aRb);
}
for (const QRhiResourceUpdateBatchPrivate::TextureMipGen &u : ud->textureMipGens) {
Q_ASSERT(u.tex->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
QD3D11CommandBuffer::Command cmd;
cmd.cmd = QD3D11CommandBuffer::Command::GenMip;
cmd.args.genMip.tex = QRHI_RES(QD3D11Texture, u.tex);
cbD->commands.append(cmd);
}
ud->free();
}
......@@ -1351,6 +1359,9 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD)
cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes,
cmd.args.resolveSubRes.format);
break;
case QD3D11CommandBuffer::Command::GenMip:
context->GenerateMips(cmd.args.genMip.tex->srv);
break;
default:
break;
}
......@@ -1574,14 +1585,27 @@ bool QD3D11Texture::build()
return false;
}
}
if (isDepth && hasMipMaps) {
qWarning("Depth texture cannot have mipmaps");
return false;
}
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
if (m_flags.testFlag(RenderTarget)) {
if (isDepth)
bindFlags |= D3D11_BIND_DEPTH_STENCIL;
else
bindFlags |= D3D11_BIND_RENDER_TARGET;
}
if (m_flags.testFlag(UsedWithGenerateMips)) {
if (isDepth) {
qWarning("Depth texture cannot have mipmaps generated");
return false;
}
bindFlags |= D3D11_BIND_RENDER_TARGET;
miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
}
D3D11_TEXTURE2D_DESC desc;
memset(&desc, 0, sizeof(desc));
......@@ -1593,7 +1617,7 @@ bool QD3D11Texture::build()
desc.SampleDesc = sampleDesc;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = bindFlags;
desc.MiscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
desc.MiscFlags = miscFlags;
HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
if (FAILED(hr)) {
......
......@@ -248,7 +248,8 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
DrawIndexed,
UpdateSubRes,
CopySubRes,
ResolveSubRes
ResolveSubRes,
GenMip
};
enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 };
Cmd cmd;
......@@ -335,6 +336,9 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
UINT srcSubRes;
DXGI_FORMAT format;
} resolveSubRes;
struct {
QD3D11Texture *tex;
} genMip;
} args;
};
......
......@@ -736,6 +736,13 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cbD->commands.append(cmd);
}
for (const QRhiResourceUpdateBatchPrivate::TextureMipGen &u : ud->textureMipGens) {
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::GenMip;
cmd.args.genMip.tex = QRHI_RES(QGles2Texture, u.tex);
cbD->commands.append(cmd);
}
ud->free();
}
......@@ -1192,6 +1199,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
}
break;
case QGles2CommandBuffer::Command::GenMip:
f->glBindTexture(cmd.args.genMip.tex->target, cmd.args.genMip.tex->texture);
f->glGenerateMipmap(cmd.args.genMip.tex->target);
break;
default:
break;
}
......
......@@ -242,7 +242,8 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
SubImage,
CompressedImage,
CompressedSubImage,
BlitFromRenderbuffer
BlitFromRenderbuffer,
GenMip
};
Cmd cmd;
union {
......@@ -361,6 +362,9 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
int dstLayer;
int dstLevel;
} blitFromRb;
struct {
QGles2Texture *tex;
} genMip;
} args;
};
......
......@@ -1768,6 +1768,35 @@ void QRhiVulkan::bufferBarrier(QRhiCommandBuffer *cb, QRhiBuffer *buf)
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
}
void QRhiVulkan::imageSubResBarrier(QRhiCommandBuffer *cb, QRhiTexture *tex,
VkImageLayout oldLayout, VkImageLayout newLayout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
int layer, int level)
{
QVkTexture *texD = QRHI_RES(QVkTexture, tex);
VkImageMemoryBarrier barrier;
memset(&barrier, 0, sizeof(barrier));
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = level;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = layer;
barrier.subresourceRange.layerCount = 1;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.image = texD->image;
df->vkCmdPipelineBarrier(QRHI_RES(QVkCommandBuffer, cb)->cb,
srcStage,
dstStage,
0, 0, nullptr, 0, nullptr,
1, &barrier);
}
void QRhiVulkan::imageBarrier(QRhiCommandBuffer *cb, QRhiTexture *tex,
VkImageLayout newLayout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
......@@ -1817,7 +1846,8 @@ void QRhiVulkan::prepareForTransferDest(QRhiCommandBuffer *cb, QVkTexture *texD)
void QRhiVulkan::prepareForTransferSrc(QRhiCommandBuffer *cb, QVkTexture *texD)
{
if (texD->layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedAsTransferSource));
Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedAsTransferSource)
|| texD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
// assume the texture was written (so block up to color output, not just fragment)
imageBarrier(cb, texD,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
......@@ -2268,6 +2298,71 @@ void QRhiVulkan::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdat
activeReadbacks.append(aRb);
}
for (const QRhiResourceUpdateBatchPrivate::TextureMipGen &u : ud->textureMipGens) {
QVkTexture *utexD = QRHI_RES(QVkTexture, u.tex);
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
int w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height();
prepareForTransferSrc(cb, utexD);
for (int level = 1; level < utexD->mipLevelCount; ++level) {
if (level > 1) {
imageSubResBarrier(cb, utexD,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, level - 1);
}
imageSubResBarrier(cb, utexD,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, level);
VkImageBlit region;
memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = level - 1;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffsets[1].x = w;
region.srcOffsets[1].y = h;
region.srcOffsets[1].z = 1;
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = level;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffsets[1].x = w >> 1;
region.dstOffsets[1].y = h >> 1;
region.dstOffsets[1].z = 1;
df->vkCmdBlitImage(cbD->cb,
utexD->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
utexD->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region,
VK_FILTER_LINEAR);
w >>= 1;
h >>= 1;
if (level == utexD->mipLevelCount - 1) {
imageSubResBarrier(cb, utexD,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, level);
}
}
finishTransferDest(cb, utexD);
}
for (const QRhiResourceUpdateBatchPrivate::TexturePrepare &u : ud->texturePrepares) {
if (u.flags.testFlag(QRhiResourceUpdateBatch::TextureRead)) {
QVkTexture *utexD = QRHI_RES(QVkTexture, u.tex);
......@@ -3339,6 +3434,8 @@ bool QVkTexture::build()
}
if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips))
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo;
memset(&allocInfo, 0, sizeof(allocInfo));
......
......@@ -426,6 +426,11 @@ public:
void finishActiveReadbacks(bool forced = false);
void bufferBarrier(QRhiCommandBuffer *cb, QRhiBuffer *buf);
void imageSubResBarrier(QRhiCommandBuffer *cb, QRhiTexture *tex,
VkImageLayout oldLayout, VkImageLayout newLayout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
int layer, int level);
void imageBarrier(QRhiCommandBuffer *cb, QRhiTexture *tex,
VkImageLayout newLayout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
......
......@@ -11,6 +11,7 @@ mtl: msaa tex+rt
mtl: resolveimage (color)
mtl: buffer upload with offset/size
mtl: color renderbuffer
mtl: mipmap generation
gl: tex formats (texture, readback)
gl: srgb
......@@ -19,8 +20,6 @@ test cubemap
test cubemap face as target
test cubemap face readback
mipmap generation?
advanced blend modes
resource import/export, what's the interop story
......@@ -67,6 +66,7 @@ dxc for d3d as an alternative to fxc?
hlsl -> dxc -> spirv -> spirv-cross hmmm...
+++ done
vk, d3d, gl: mipmap gen
msaa swapchain readback
d3d: resolveimage (color)
vk: resolveimage (color)
......
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