Skip to content

Commit 5c37108

Browse files
authored
Add GEN_MIPMAPPABLE TextureUsage (#9042)
This will allow for more backend-specific usage mapping then before - where BLIT_SRC and BLIT_DST are assumed for textures whose mipmaps will be generated. These mappings have been introduced in metal, vulkan, webgpu.
1 parent 2c7064e commit 5c37108

File tree

10 files changed

+83
-53
lines changed

10 files changed

+83
-53
lines changed

filament/backend/include/backend/DriverEnums.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,7 @@ enum class TextureUsage : uint16_t {
10681068
BLIT_SRC = 0x0040, //!< Texture can be used the source of a blit()
10691069
BLIT_DST = 0x0080, //!< Texture can be used the destination of a blit()
10701070
PROTECTED = 0x0100, //!< Texture can be used for protected content
1071+
GEN_MIPMAPPABLE = 0x0200, //!< Texture can be used with generateMipmaps()
10711072
DEFAULT = UPLOADABLE | SAMPLEABLE, //!< Default texture usage
10721073
ALL_ATTACHMENTS = COLOR_ATTACHMENT | DEPTH_ATTACHMENT | STENCIL_ATTACHMENT | SUBPASS_INPUT, //!< Mask of all attachments
10731074
};

filament/backend/src/metal/MetalHandles.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
102102
if (any(usage & TextureUsage::BLIT_SRC)) {
103103
u |= MTLTextureUsageShaderRead;
104104
}
105+
if (any(usage & TextureUsage::GEN_MIPMAPPABLE)) {
106+
u |= (MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead);
107+
}
105108

106109
return MTLTextureUsage(u);
107110
}

filament/backend/src/vulkan/VulkanTexture.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
162162
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
163163
}
164164

165+
if (any(tusage & TextureUsage::GEN_MIPMAPPABLE)) {
166+
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
167+
}
168+
165169
// Determine if we can use the transient usage flag combined with lazily allocated memory.
166170
const bool useTransientAttachment =
167171
// Lazily allocated memory is available.

filament/backend/src/webgpu/WebGPUTexture.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,13 @@ namespace {
143143
retUsage |= wgpu::TextureUsage::RenderAttachment;
144144
}
145145
if (any(TextureUsage::BLIT_SRC & fUsage)) {
146-
retUsage |= wgpu::TextureUsage::RenderAttachment;
146+
retUsage |= wgpu::TextureUsage::TextureBinding;
147147
}
148148
if (any(TextureUsage::BLIT_DST & fUsage)) {
149149
retUsage |= wgpu::TextureUsage::RenderAttachment;
150-
retUsage |= wgpu::TextureUsage::TextureBinding;
150+
}
151+
if (any(TextureUsage::GEN_MIPMAPPABLE & fUsage)) {
152+
retUsage |= (wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding);
151153
}
152154
// WGPU Render attachment covers either color or stencil situation dependant
153155
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If

filament/src/details/Engine.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,8 @@ class FEngine : public Engine {
733733
CORRECTNESS_ASSERTION_DEFAULT;
734734
bool assert_material_instance_texture_descriptor_set_compatible =
735735
CORRECTNESS_ASSERTION_DEFAULT;
736+
bool assert_texture_format_mipmappable = CORRECTNESS_ASSERTION_DEFAULT;
737+
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
736738
} debug;
737739
} engine;
738740
struct {
@@ -787,11 +789,17 @@ class FEngine : public Engine {
787789
"Assert that the attribute stride of a vertex buffer is a multiple of 4.",
788790
&features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4, false },
789791
{ "backend.vulkan.enable_staging_buffer_bypass",
790-
"vulkan: enable a staging bypass logic for unified memory architecture",
792+
"vulkan: enable a staging bypass logic for unified memory architecture.",
791793
&features.backend.vulkan.enable_staging_buffer_bypass, false },
792794
{ "engine.debug.assert_material_instance_texture_descriptor_set_compatible",
793795
"Assert that the textures in a material instance are compatible with descriptor set.",
794796
&features.engine.debug.assert_material_instance_texture_descriptor_set_compatible, false },
797+
{ "engine.debug.assert_texture_format_mipmappable",
798+
"Assert if a texture (with levels > 1) that the format is mipmappable.",
799+
&features.engine.debug.assert_texture_format_mipmappable, false },
800+
{ "engine.debug.assert_texture_can_generate_mipmap",
801+
"Assert if a texture has the correct usage set for generating mipmaps.",
802+
&features.engine.debug.assert_texture_can_generate_mipmap, false },
795803
}};
796804

797805
utils::Slice<const FeatureFlag> getFeatureFlags() const noexcept {

filament/src/details/Texture.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,29 @@ Texture* Texture::Builder::build(Engine& engine) {
242242

243243
if (mImpl->mUsage == TextureUsage::NONE) {
244244
mImpl->mUsage = TextureUsage::DEFAULT;
245-
if (mImpl->mLevels > 1 &&
246-
(mImpl->mWidth > 1 || mImpl->mHeight > 1) &&
247-
!mImpl->mExternal) {
248-
const bool formatMipmappable =
249-
downcast(engine).getDriverApi().isTextureFormatMipmappable(mImpl->mFormat);
250-
if (formatMipmappable) {
251-
// by default mipmappable textures have the BLIT usage bits set
252-
mImpl->mUsage |= TextureUsage::BLIT_SRC | TextureUsage::BLIT_DST;
253-
}
254-
}
245+
}
246+
247+
auto const& featureFlags = downcast(engine).features.engine.debug;
248+
249+
bool const formatMipmappable =
250+
downcast(engine).getDriverApi().isTextureFormatMipmappable(mImpl->mFormat);
251+
252+
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(mImpl->mLevels == 1 || formatMipmappable,
253+
featureFlags.assert_texture_format_mipmappable)
254+
<< "Texture levels is > 1 (levels=" << +mImpl->mLevels
255+
<< " dim=" << mImpl->mWidth << "x" << mImpl->mHeight
256+
<< ", but the format ("
257+
<< int(mImpl->mFormat) << ") "
258+
<< " is not mipmppable";
259+
260+
// TODO: This exists for backwards compatibility, but should remove when safe.
261+
if (!featureFlags.assert_texture_can_generate_mipmap &&
262+
// Guess whether GEN_MIPMAPPABLE should be added or not based the following criteria.
263+
(formatMipmappable &&
264+
mImpl->mLevels > 1 &&
265+
(mImpl->mWidth > 1 || mImpl->mHeight > 1) &&
266+
!mImpl->mExternal)) {
267+
mImpl->mUsage |= TextureUsage::GEN_MIPMAPPABLE;
255268
}
256269

257270
// TODO: remove in a future filament release.
@@ -640,6 +653,9 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept {
640653
FILAMENT_CHECK_PRECONDITION(formatMipmappable)
641654
<< "Texture format " << (unsigned)mFormat << " is not mipmappable.";
642655

656+
FILAMENT_CHECK_PRECONDITION(any(mUsage & TextureUsage::GEN_MIPMAPPABLE))
657+
<< "Texture usage does not have GEN_MIPMAPPABLE set";
658+
643659
if (mLevelCount < 2 || (mWidth == 1 && mHeight == 1)) {
644660
return;
645661
}

libs/filamentapp/src/IBL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ bool IBL::loadFromEquirect(Path const& path) {
116116
.levels(0xff)
117117
.format(Texture::InternalFormat::R11F_G11F_B10F)
118118
.sampler(Texture::Sampler::SAMPLER_2D)
119+
.usage(Texture::Usage::DEFAULT | Texture::Usage::GEN_MIPMAPPABLE)
119120
.build(mEngine);
120121

121122
equirect->setImage(mEngine, 0, std::move(buffer));
@@ -131,7 +132,7 @@ bool IBL::loadFromEquirect(Path const& path) {
131132

132133
mTexture = specularFilter(mSkyboxTexture);
133134

134-
mFogTexture = irradianceFilter({ .generateMipmap = false }, mSkyboxTexture);
135+
mFogTexture = irradianceFilter({ .generateMipmap = true }, mSkyboxTexture);
135136
mFogTexture->generateMipmaps(mEngine);
136137

137138
mIndirectLight = IndirectLight::Builder()

libs/gltfio/src/StbProvider.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Texture* StbProvider::pushTexture(const uint8_t* data, size_t byteCount,
101101
.height(height)
102102
.levels(0xff)
103103
.format(any(flags & TextureFlags::sRGB) ? InternalFormat::SRGB8_A8 : InternalFormat::RGBA8)
104+
.usage(Texture::Usage::DEFAULT | Texture::Usage::GEN_MIPMAPPABLE)
104105
.build(*mEngine);
105106

106107
if (texture == nullptr) {

libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ class UTILS_PUBLIC IBLPrefilterContext {
227227
filament::Texture* outIrradianceTexture = nullptr);
228228

229229
private:
230-
filament::Texture* createIrradianceTexture();
231230
IBLPrefilterContext& mContext;
232231
filament::Material* mKernelMaterial = nullptr;
233232
filament::Texture* mKernelTexture = nullptr;
@@ -325,7 +324,6 @@ class UTILS_PUBLIC IBLPrefilterContext {
325324
// TODO: add a callback for when the processing is done?
326325

327326
private:
328-
filament::Texture* createReflectionsTexture();
329327
IBLPrefilterContext& mContext;
330328
filament::Material* mKernelMaterial = nullptr;
331329
filament::Texture* mKernelTexture = nullptr;

libs/iblprefilter/src/IBLPrefilterContext.cpp

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,11 @@ static void cleanupMaterialInstance(MaterialInstance const* mi, Engine& engine,
9191
engine.destroy(mi);
9292
}
9393

94-
} // anonymous
94+
constexpr Texture::Usage COMMON_USAGE =
95+
Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE;
96+
constexpr Texture::Usage MIPMAP_USAGE = Texture::Usage::GEN_MIPMAPPABLE;
97+
98+
} // namespace
9599

96100

97101
IBLPrefilterContext::IBLPrefilterContext(Engine& engine)
@@ -258,7 +262,7 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()(
258262
outCube = Texture::Builder()
259263
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
260264
.format(Texture::InternalFormat::R11F_G11F_B10F)
261-
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
265+
.usage(COMMON_USAGE | MIPMAP_USAGE)
262266
.width(256).height(256).levels(0xFF)
263267
.build(engine);
264268
}
@@ -347,7 +351,7 @@ IBLPrefilterContext::IrradianceFilter::IrradianceFilter(IBLPrefilterContext& con
347351
mKernelTexture = Texture::Builder()
348352
.sampler(Texture::Sampler::SAMPLER_2D)
349353
.format(Texture::InternalFormat::RGBA16F)
350-
.usage(Texture::Usage::SAMPLEABLE | Texture::Usage::COLOR_ATTACHMENT)
354+
.usage(COMMON_USAGE)
351355
.width(1)
352356
.height(mSampleCount)
353357
.build(engine);
@@ -421,7 +425,16 @@ Texture* IBLPrefilterContext::IrradianceFilter::operator()(Options options,
421425
<< "environmentCubemap must have " << +maxLevelCount << " mipmap levels allocated.";
422426

423427
if (outIrradianceTexture == nullptr) {
424-
outIrradianceTexture = createIrradianceTexture();
428+
outIrradianceTexture =
429+
Texture::Builder()
430+
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
431+
.format(Texture::InternalFormat::R11F_G11F_B10F)
432+
.usage(COMMON_USAGE |
433+
(options.generateMipmap ? MIPMAP_USAGE : Texture::Usage::NONE))
434+
.width(256)
435+
.height(256)
436+
.levels(0xff)
437+
.build(mContext.mEngine);
425438
}
426439

427440
FILAMENT_CHECK_PRECONDITION(
@@ -509,19 +522,6 @@ Texture* IBLPrefilterContext::IrradianceFilter::operator()(
509522
return operator()({}, environmentCubemap, outIrradianceTexture);
510523
}
511524

512-
Texture* IBLPrefilterContext::IrradianceFilter::createIrradianceTexture() {
513-
Engine& engine = mContext.mEngine;
514-
515-
Texture* const outCubemap = Texture::Builder()
516-
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
517-
.format(Texture::InternalFormat::R11F_G11F_B10F)
518-
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
519-
.width(256).height(256).levels(0xff)
520-
.build(engine);
521-
522-
return outCubemap;
523-
}
524-
525525
// ------------------------------------------------------------------------------------------------
526526

527527
IBLPrefilterContext::SpecularFilter::SpecularFilter(IBLPrefilterContext& context, Config config)
@@ -557,7 +557,7 @@ IBLPrefilterContext::SpecularFilter::SpecularFilter(IBLPrefilterContext& context
557557
mKernelTexture = Texture::Builder()
558558
.sampler(Texture::Sampler::SAMPLER_2D)
559559
.format(Texture::InternalFormat::RGBA16F)
560-
.usage(Texture::Usage::SAMPLEABLE | Texture::Usage::COLOR_ATTACHMENT)
560+
.usage(COMMON_USAGE)
561561
.width(mLevelCount)
562562
.height(mSampleCount)
563563
.build(engine);
@@ -621,24 +621,6 @@ IBLPrefilterContext::SpecularFilter::operator=(SpecularFilter&& rhs) noexcept {
621621
return *this;
622622
}
623623

624-
Texture* IBLPrefilterContext::SpecularFilter::createReflectionsTexture() {
625-
Engine& engine = mContext.mEngine;
626-
627-
const uint8_t levels = mLevelCount;
628-
629-
// default texture is 256 or larger to accommodate the level count requested
630-
const uint32_t dim = std::max(256u, 1u << (levels - 1u));
631-
632-
Texture* const outCubemap = Texture::Builder()
633-
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
634-
.format(Texture::InternalFormat::R11F_G11F_B10F)
635-
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
636-
.width(dim).height(dim).levels(levels)
637-
.build(engine);
638-
639-
return outCubemap;
640-
}
641-
642624
UTILS_NOINLINE
643625
Texture* IBLPrefilterContext::SpecularFilter::operator()(
644626
Texture const* environmentCubemap, Texture* outReflectionsTexture) {
@@ -665,7 +647,21 @@ Texture* IBLPrefilterContext::SpecularFilter::operator()(
665647
<< "environmentCubemap must have " << +maxLevelCount << " mipmap levels allocated.";
666648

667649
if (outReflectionsTexture == nullptr) {
668-
outReflectionsTexture = createReflectionsTexture();
650+
const uint8_t levels = mLevelCount;
651+
652+
// default texture is 256 or larger to accommodate the level count requested
653+
const uint32_t dim = std::max(256u, 1u << (levels - 1u));
654+
655+
outReflectionsTexture =
656+
Texture::Builder()
657+
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
658+
.format(Texture::InternalFormat::R11F_G11F_B10F)
659+
.usage(COMMON_USAGE |
660+
(options.generateMipmap ? MIPMAP_USAGE : Texture::Usage::NONE))
661+
.width(dim)
662+
.height(dim)
663+
.levels(levels)
664+
.build(mContext.mEngine);
669665
}
670666

671667
FILAMENT_CHECK_PRECONDITION(

0 commit comments

Comments
 (0)