diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index f76aac6e8a..dbb4dfd24d 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -26,6 +26,7 @@ A new header is inserted each time a *tag* is created.
- Fix several issues related to multi-view support. (⚠️ **API breakage**) removed
`View::setClearColor()`, a similar functionality is now handled by `Renderer::setClearOptions()`
and `Skybox`, the later now can be set to a constant color.
+- Textures can now be swizzled.
## v1.5.2
diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp
index 8b53491962..8c33a4d170 100644
--- a/android/filament-android/src/main/cpp/Texture.cpp
+++ b/android/filament-android/src/main/cpp/Texture.cpp
@@ -113,6 +113,14 @@ Java_com_google_android_filament_Texture_nBuilderUsage(JNIEnv*, jclass,
builder->usage((Texture::Usage) flags);
}
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_android_filament_Texture_nBuilderSwizzle(JNIEnv *, jclass ,
+ jlong nativeBuilder, jint r, jint g, jint b, jint a) {
+ Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
+ builder->swizzle(
+ (Texture::Swizzle)r, (Texture::Swizzle)g, (Texture::Swizzle)b, (Texture::Swizzle)a);
+}
+
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Texture_nBuilderBuild(JNIEnv*, jclass,
jlong nativeBuilder, jlong nativeEngine) {
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Texture.java b/android/filament-android/src/main/java/com/google/android/filament/Texture.java
index fe8e074de1..069187d55f 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Texture.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Texture.java
@@ -297,6 +297,18 @@ public class Texture {
USHORT_565,
}
+ /**
+ * Texture swizzling channels
+ */
+ public enum Swizzle {
+ SUBSTITUTE_ZERO, //!< specified component is substituted with 0
+ SUBSTITUTE_ONE, //!< specified component is substituted with 1
+ CHANNEL_0, //!< specified component taken from channel 0
+ CHANNEL_1, //!< specified component taken from channel 1
+ CHANNEL_2, //!< specified component taken from channel 2
+ CHANNEL_3 //!< specified component taken from channel 3
+ }
+
/**
* A descriptor to an image in main memory, typically used to transfer image data from the CPU
* to the GPU.
@@ -645,6 +657,21 @@ public class Texture {
return this;
}
+ /**
+ * Specifies how a texture's channels map to color components
+ *
+ * @param r texture channel for red component
+ * @param g texture channel for green component
+ * @param b texture channel for blue component
+ * @param a texture channel for alpha component
+ * @return This Builder, for chaining calls.
+ */
+ @NonNull
+ public Builder swizzle(@NonNull Swizzle r, @NonNull Swizzle g, @NonNull Swizzle b, @NonNull Swizzle a) {
+ nBuilderSwizzle(mNativeBuilder, r.ordinal(), g.ordinal(), b.ordinal(), a.ordinal());
+ return this;
+ }
+
/**
* Creates a new Texture instance.
* @param engine The {@link Engine} to associate this Texture with.
@@ -1061,6 +1088,7 @@ public class Texture {
private static native void nBuilderSampler(long nativeBuilder, int sampler);
private static native void nBuilderFormat(long nativeBuilder, int format);
private static native void nBuilderUsage(long nativeBuilder, int flags);
+ private static native void nBuilderSwizzle(long nativeBuilder, int r, int g, int b, int a);
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
private static native int nGetWidth(long nativeTexture, int level);
diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h
index 546f192883..99fefc181c 100644
--- a/filament/backend/include/backend/DriverEnums.h
+++ b/filament/backend/include/backend/DriverEnums.h
@@ -477,6 +477,16 @@ enum class TextureUsage : uint8_t {
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
};
+//! Texture swizzle
+enum class TextureSwizzle {
+ SUBSTITUTE_ZERO,
+ SUBSTITUTE_ONE,
+ CHANNEL_0,
+ CHANNEL_1,
+ CHANNEL_2,
+ CHANNEL_3
+};
+
//! returns whether this format a compressed format
static constexpr bool isCompressedFormat(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11;
diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc
index 498a4b550e..26d91e0ca2 100644
--- a/filament/backend/include/private/backend/DriverAPI.inc
+++ b/filament/backend/include/private/backend/DriverAPI.inc
@@ -292,6 +292,13 @@ DECL_DRIVER_API_N(updateSamplerGroup,
backend::SamplerGroupHandle, ubh,
backend::SamplerGroup&&, samplerGroup)
+DECL_DRIVER_API_N(setTextureSwizzle,
+ backend::TextureHandle, th,
+ backend::TextureSwizzle, r,
+ backend::TextureSwizzle, g,
+ backend::TextureSwizzle, b,
+ backend::TextureSwizzle, a)
+
DECL_DRIVER_API_N(update2DImage,
backend::TextureHandle, th,
uint32_t, level,
diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm
index e3e809f206..e93ef1b4fe 100644
--- a/filament/backend/src/metal/MetalDriver.mm
+++ b/filament/backend/src/metal/MetalDriver.mm
@@ -575,6 +575,11 @@ void MetalDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&
ib->buffer.copyIntoBuffer(data.buffer, data.size);
}
+void MetalDriver::setTextureSwizzle(Handle th,
+ TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
+ // TODO: implement setTextureSwizzle
+}
+
void MetalDriver::update2DImage(Handle th, uint32_t level, uint32_t xoffset,
uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& data) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp
index b1d2e8f87b..378177fff4 100644
--- a/filament/backend/src/noop/NoopDriver.cpp
+++ b/filament/backend/src/noop/NoopDriver.cpp
@@ -157,6 +157,10 @@ void NoopDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&&
scheduleDestroy(std::move(p));
}
+void NoopDriver::setTextureSwizzle(Handle th,
+ TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
+}
+
void NoopDriver::update2DImage(Handle th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
diff --git a/filament/backend/src/opengl/GLUtils.h b/filament/backend/src/opengl/GLUtils.h
index 3af7321606..5e05f7deed 100644
--- a/filament/backend/src/opengl/GLUtils.h
+++ b/filament/backend/src/opengl/GLUtils.h
@@ -274,6 +274,24 @@ constexpr inline GLenum getType(backend::PixelDataType type) noexcept {
}
}
+constexpr inline GLenum getSwizzleChannel(backend::TextureSwizzle c) noexcept {
+ using TextureSwizzle = backend::TextureSwizzle;
+ switch (c) {
+ case TextureSwizzle::SUBSTITUTE_ZERO:
+ return GL_ZERO;
+ case TextureSwizzle::SUBSTITUTE_ONE:
+ return GL_ONE;
+ case TextureSwizzle::CHANNEL_0:
+ return GL_RED;
+ case TextureSwizzle::CHANNEL_1:
+ return GL_GREEN;
+ case TextureSwizzle::CHANNEL_2:
+ return GL_BLUE;
+ case TextureSwizzle::CHANNEL_3:
+ return GL_ALPHA;
+ }
+}
+
// clang looses it on this one, and generates a huge jump table when
// inlined. So we don't mark it as inline (only constexpr) which solves the problem,
// strangely, when not inlined, clang simply generates an array lookup.
diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp
index 58a0486204..9b6ec9a0de 100644
--- a/filament/backend/src/opengl/OpenGLDriver.cpp
+++ b/filament/backend/src/opengl/OpenGLDriver.cpp
@@ -1603,6 +1603,19 @@ void OpenGLDriver::updateSamplerGroup(Handle sbh,
*sb->sb = std::move(samplerGroup); // NOLINT(performance-move-const-arg)
}
+void OpenGLDriver::setTextureSwizzle(Handle th,
+ TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
+ DEBUG_MARKER()
+ GLTexture* t = handle_cast(th);
+ bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
+ mContext.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
+ glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_R, getSwizzleChannel(r));
+ glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_G, getSwizzleChannel(g));
+ glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_B, getSwizzleChannel(b));
+ glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_A, getSwizzleChannel(a));
+ CHECK_GL_ERROR(utils::slog.e)
+}
+
void OpenGLDriver::update2DImage(Handle th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp
index b0a666d6a2..925bbb4605 100644
--- a/filament/backend/src/vulkan/VulkanDriver.cpp
+++ b/filament/backend/src/vulkan/VulkanDriver.cpp
@@ -691,6 +691,11 @@ void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor
scheduleDestroy(std::move(p));
}
+void VulkanDriver::setTextureSwizzle(Handle th,
+ TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
+ // TODO: implement setTextureSwizzle
+}
+
void VulkanDriver::update2DImage(Handle th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h
index babae5949d..d404614c39 100644
--- a/filament/include/filament/Texture.h
+++ b/filament/include/filament/Texture.h
@@ -79,6 +79,7 @@ public:
using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format
using FaceOffsets = backend::FaceOffsets; //!< Cube map faces offsets
using Usage = backend::TextureUsage; //!< Usage affects texel layout
+ using Swizzle = backend::TextureSwizzle; //!< Texture swizzle
static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept;
@@ -175,6 +176,17 @@ public:
*/
Builder& usage(Usage usage) noexcept;
+ /**
+ * Specifies how a texture's channels map to color components
+ *
+ * @param r texture channel for red component
+ * @param g texture channel for green component
+ * @param b texture channel for blue component
+ * @param a texture channel for alpha component
+ * @return This Builder, for chaining calls.
+ */
+ Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
+
/**
* Creates the Texture object and returns a pointer to it.
*
diff --git a/filament/src/Texture.cpp b/filament/src/Texture.cpp
index 5ccb442aad..4c06b6539b 100644
--- a/filament/src/Texture.cpp
+++ b/filament/src/Texture.cpp
@@ -47,6 +47,10 @@ struct Texture::BuilderDetails {
Sampler mTarget = Sampler::SAMPLER_2D;
InternalFormat mFormat = InternalFormat::RGBA8;
Usage mUsage = Usage::DEFAULT;
+ bool mTextureIsSwizzled = false;
+ std::array mSwizzle = {
+ Swizzle::CHANNEL_0, Swizzle::CHANNEL_1,
+ Swizzle::CHANNEL_2, Swizzle::CHANNEL_3 };
};
using BuilderType = Texture;
@@ -99,6 +103,12 @@ Texture::Builder& Texture::Builder::import(intptr_t id) noexcept {
return *this;
}
+Texture::Builder& Texture::Builder::swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept {
+ mImpl->mTextureIsSwizzled = true;
+ mImpl->mSwizzle = { r, g, b, a };
+ return *this;
+}
+
Texture* Texture::Builder::build(Engine& engine) {
FEngine::assertValid(engine, __PRETTY_FUNCTION__);
if (!ASSERT_POSTCONDITION_NON_FATAL(Texture::isTextureFormatSupported(engine, mImpl->mFormat),
@@ -130,6 +140,11 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
mHandle = driver.importTexture(builder->mImportedId,
mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage);
}
+ if (UTILS_UNLIKELY(builder->mTextureIsSwizzled)) {
+ driver.setTextureSwizzle(mHandle,
+ builder->mSwizzle[0], builder->mSwizzle[1], builder->mSwizzle[2],
+ builder->mSwizzle[3]);
+ }
}
// frees driver resources, object becomes invalid