Skip to content

Commit

Permalink
implement generateMipmaps for the vulkan backend
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelflinger committed Nov 15, 2023
1 parent f700598 commit c6fde5f
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 138 deletions.
1 change: 0 additions & 1 deletion filament/backend/include/private/backend/DriverAPI.inc
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, canGenerateMipmaps)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
Expand Down
20 changes: 11 additions & 9 deletions filament/backend/src/metal/MetalDriver.mm
Original file line number Diff line number Diff line change
Expand Up @@ -865,10 +865,6 @@
tex->generateMipmaps();
}

bool MetalDriver::canGenerateMipmaps() {
return true;
}

void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"updateSamplerGroup must be called outside of a render pass.");
Expand Down Expand Up @@ -1307,6 +1303,9 @@
assert_invariant(srcTexture);
assert_invariant(dstTexture);

ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
"resolve() cannot be invoked inside a render pass.");

ASSERT_PRECONDITION(
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
"invalid resolve: src and dst sizes don't match");
Expand Down Expand Up @@ -1336,11 +1335,6 @@
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
math::uint2 size) {

auto* const srcTexture = handle_cast<MetalTexture>(src);
auto* const dstTexture = handle_cast<MetalTexture>(dst);
assert_invariant(srcTexture);
assert_invariant(dstTexture);

enum class FormatType { COLOR, DEPTH, STENCIL, DEPTH_STENCIL };
auto getFormatType = [](TextureFormat format) -> FormatType {
bool const depth = isDepthFormat(format);
Expand All @@ -1356,6 +1350,14 @@
t == MTLTextureType2DArray;
};

auto* const srcTexture = handle_cast<MetalTexture>(src);
auto* const dstTexture = handle_cast<MetalTexture>(dst);
assert_invariant(srcTexture);
assert_invariant(dstTexture);

ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
"blit() cannot be invoked inside a render pass.");

ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
"texture doesn't have BLIT_DST");

Expand Down
4 changes: 0 additions & 4 deletions filament/backend/src/noop/NoopDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,6 @@ void NoopDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {

void NoopDriver::generateMipmaps(Handle<HwTexture> th) { }

bool NoopDriver::canGenerateMipmaps() {
return true;
}

void NoopDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
BufferDescriptor&& data) {
scheduleDestroy(std::move(data));
Expand Down
4 changes: 0 additions & 4 deletions filament/backend/src/opengl/OpenGLDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2264,10 +2264,6 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
CHECK_GL_ERROR(utils::slog.e)
}

bool OpenGLDriver::canGenerateMipmaps() {
return true;
}

void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
Expand Down
16 changes: 8 additions & 8 deletions filament/backend/src/vulkan/VulkanBlitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
.aspectMask = aspect,
.baseMipLevel = src.level,
.levelCount = 1,
.baseArrayLayer = src.layer,
.layerCount = 1,
.baseArrayLayer = uint32_t(src.layer + srcRect[0].z),
.layerCount = uint32_t(srcRect[1].z),
};

const VkImageSubresourceRange dstRange = {
.aspectMask = aspect,
.baseMipLevel = dst.level,
.levelCount = 1,
.baseArrayLayer = dst.layer,
.layerCount = 1,
.baseArrayLayer = uint32_t(dst.layer + dstRect[0].z),
.layerCount = uint32_t(dstRect[1].z),
};

VulkanLayout oldSrcLayout = src.getLayout();
Expand All @@ -71,10 +71,10 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
dst.texture->transitionLayout(cmdbuffer, dstRange, VulkanLayout::TRANSFER_DST);

const VkImageBlit blitRegions[1] = {{
.srcSubresource = {aspect, src.level, src.layer, 1},
.srcOffsets = {srcRect[0], srcRect[1]},
.dstSubresource = {aspect, dst.level, dst.layer, 1},
.dstOffsets = {dstRect[0], dstRect[1]},
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffsets = { srcRect[0], srcRect[1] },
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffsets = { dstRect[0], dstRect[1] },
}};
vkCmdBlitImage(cmdbuffer,
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
Expand Down
97 changes: 56 additions & 41 deletions filament/backend/src/vulkan/VulkanDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,10 +953,26 @@ void VulkanDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
}

void VulkanDriver::generateMipmaps(Handle<HwTexture> th) { }

bool VulkanDriver::canGenerateMipmaps() {
return false;
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
auto* const t = mResourceAllocator.handle_cast<VulkanTexture*>(th);
uint8_t level = 0;
int32_t srcw = int32_t(t->width);
int32_t srch = int32_t(t->height);
do {
int32_t const dstw = std::max(srcw >> 1, 1);
int32_t const dsth = std::max(srch >> 1, 1);
const VkOffset3D srcOffsets[2] = { { 0, 0, 0 }, { srcw, srch, int32_t(t->depth) }};
const VkOffset3D dstOffsets[2] = { { 0, 0, 0 }, { dstw, dsth, int32_t(t->depth) }};

mBlitter.blit(VK_FILTER_LINEAR,
{ .texture = t, .level = uint8_t(level + 1), .layer = 0 }, dstOffsets,
{ .texture = t, .level = uint8_t(level ), .layer = 0 }, srcOffsets);

level++;
srcw = dstw;
srch = dsth;
} while ((srcw > 1 || srch > 1) && level < t->levels);
t->setPrimaryRange(0, t->levels - 1);
}

void VulkanDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
Expand Down Expand Up @@ -1443,6 +1459,9 @@ void VulkanDriver::resolve(
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("resolve");

ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
"resolve() cannot be invoked inside a render pass.");

auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
assert_invariant(srcTexture);
Expand Down Expand Up @@ -1479,11 +1498,8 @@ void VulkanDriver::blit(
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blit");

assert_invariant(mCurrentRenderPass.renderPass == VK_NULL_HANDLE);
if (UTILS_UNLIKELY(mCurrentRenderPass.renderPass)) {
utils::slog.e << "Blits cannot be invoked inside a render pass." << utils::io::endl;
return;
}
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
"blit() cannot be invoked inside a render pass.");

auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
Expand All @@ -1499,17 +1515,16 @@ void VulkanDriver::blit(

// The Y inversion below makes it so that Vk matches GL and Metal.

const int32_t srcLeft = srcOrigin.x;
const int32_t srcTop = srcTexture->height - srcOrigin.y - size.y;
const int32_t srcRight = srcOrigin.x + size.x;
const int32_t srcBottom = srcTop + size.y;
const VkOffset3D srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};

const int32_t dstLeft = dstOrigin.x;
const int32_t dstTop = dstTexture->height - dstOrigin.y - size.y;
const int32_t dstRight = dstOrigin.x + size.x;
const int32_t dstBottom = dstTop + size.y;
const VkOffset3D dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
auto const srcLeft = int32_t(srcOrigin.x);
auto const dstLeft = int32_t(dstOrigin.x);
auto const srcTop = int32_t(srcTexture->height - (srcOrigin.y + size.y));
auto const dstTop = int32_t(dstTexture->height - (dstOrigin.y + size.y));
auto const srcRight = int32_t(srcOrigin.x + size.x);
auto const dstRight = int32_t(dstOrigin.x + size.x);
auto const srcBottom = int32_t(srcTop + size.y);
auto const dstBottom = int32_t(dstTop + size.y);
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};

// no scallng guaranteed
mBlitter.blit(VK_FILTER_NEAREST,
Expand All @@ -1519,18 +1534,17 @@ void VulkanDriver::blit(
FVK_SYSTRACE_END();
}

void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
SamplerMagFilter filter) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blitDEPRECATED");

// TODO: Currently blitDEPRECATED is only used for Texture::generateMipmaps(). This should
// instead be implemented in VulkanDriver::generateMipmaps(); both GL and Metal support
// this. This would allow use to remove blitDEPRECATED.
// Note: blitDEPRECATED is also use for Renderer::copyFrame(), but that's not supported
// with the vulkan backend (and never was).
// Note: blitDEPRECATED is only used for Renderer::copyFrame()

assert_invariant(mCurrentRenderPass.renderPass == VK_NULL_HANDLE);
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
"blitDEPRECATED() cannot be invoked inside a render pass.");

ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
"blitDEPRECATED only supports COLOR0");
Expand All @@ -1547,22 +1561,23 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, Handle<HwRenderTarg

// The Y inversion below makes it so that Vk matches GL and Metal.

const VkExtent2D srcExtent = srcTarget->getExtent();
const int32_t srcLeft = srcRect.left;
const int32_t srcTop = srcExtent.height - srcRect.bottom - srcRect.height;
const int32_t srcRight = srcRect.left + srcRect.width;
const int32_t srcBottom = srcTop + srcRect.height;
const VkOffset3D srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
VkExtent2D const srcExtent = srcTarget->getExtent();
VkExtent2D const dstExtent = dstTarget->getExtent();

const VkExtent2D dstExtent = dstTarget->getExtent();
const int32_t dstLeft = dstRect.left;
const int32_t dstTop = dstExtent.height - dstRect.bottom - dstRect.height;
const int32_t dstRight = dstRect.left + dstRect.width;
const int32_t dstBottom = dstTop + dstRect.height;
const VkOffset3D dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
auto const srcLeft = int32_t(srcRect.left);
auto const dstLeft = int32_t(dstRect.left);
auto const dstTop = int32_t(dstExtent.height - (dstRect.bottom + dstRect.height));
auto const srcTop = int32_t(srcExtent.height - (srcRect.bottom + srcRect.height));
auto const dstRight = int32_t(dstRect.left + dstRect.width);
auto const srcRight = int32_t(srcRect.left + srcRect.width);
auto const dstBottom = int32_t(dstTop + dstRect.height);
auto const srcBottom = int32_t(srcTop + srcRect.height);
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};

mBlitter.blit(vkfilter,
dstTarget->getColor(0), dstOffsets, srcTarget->getColor(0), srcOffsets);
dstTarget->getColor(0), dstOffsets,
srcTarget->getColor(0), srcOffsets);

FVK_SYSTRACE_END();
}
Expand Down
72 changes: 1 addition & 71 deletions filament/src/details/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,77 +412,7 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept {
return;
}

if (engine.getDriverApi().canGenerateMipmaps()) {
// This is called for GL, GLES and Metal
engine.getDriverApi().generateMipmaps(mHandle);
return;
}

// The code below is used only on Vulkan

auto generateMipsForLayer = [this, &engine](TargetBufferInfo proto) {
FEngine::DriverApi& driver = engine.getDriverApi();

// Wrap miplevel 0 in a render target so that we can use it as a blit source.
uint8_t level = 0;
uint32_t srcw = mWidth;
uint32_t srch = mHeight;
proto.handle = mHandle;
proto.level = level++;
backend::Handle<backend::HwRenderTarget> srcrth = driver.createRenderTarget(
TargetBufferFlags::COLOR, srcw, srch, mSampleCount, proto, {}, {});

// Perform a blit for all miplevels down to 1x1.
backend::Handle<backend::HwRenderTarget> dstrth;
do {
uint32_t const dstw = std::max(srcw >> 1u, 1u);
uint32_t const dsth = std::max(srch >> 1u, 1u);
proto.level = level++;
dstrth = driver.createRenderTarget(
TargetBufferFlags::COLOR, dstw, dsth, mSampleCount, proto, {}, {});
driver.blitDEPRECATED(TargetBufferFlags::COLOR,
dstrth, { 0, 0, dstw, dsth },
srcrth, { 0, 0, srcw, srch },
SamplerMagFilter::LINEAR);
driver.destroyRenderTarget(srcrth);
srcrth = dstrth;
srcw = dstw;
srch = dsth;
} while ((srcw > 1 || srch > 1) && level < mLevelCount);
driver.destroyRenderTarget(dstrth);
};

switch (mTarget) {
case SamplerType::SAMPLER_2D:
generateMipsForLayer({});
break;
case SamplerType::SAMPLER_2D_ARRAY:
UTILS_NOUNROLL
for (uint16_t layer = 0, c = mDepth; layer < c; ++layer) {
generateMipsForLayer({ .layer = layer });
}
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
UTILS_NOUNROLL
for (uint16_t layer = 0, c = mDepth * 6; layer < c; ++layer) {
generateMipsForLayer({ .layer = layer });
}
break;
case SamplerType::SAMPLER_CUBEMAP:
UTILS_NOUNROLL
for (uint8_t face = 0; face < 6; ++face) {
generateMipsForLayer({ .layer = face });
}
break;
case SamplerType::SAMPLER_EXTERNAL:
// not mipmapable
break;
case SamplerType::SAMPLER_3D:
// TODO: handle SAMPLER_3D -- this can't be done with a 2D blit, this would require
// a fragment shader
slog.w << "Texture::generateMipmap does not support SAMPLER_3D yet on this h/w." << io::endl;
break;
}
engine.getDriverApi().generateMipmaps(mHandle);
}

bool FTexture::isTextureFormatSupported(FEngine& engine, InternalFormat format) noexcept {
Expand Down

0 comments on commit c6fde5f

Please sign in to comment.