From ed326e1e68ece56842adf73ce17a35cd597b5fcd Mon Sep 17 00:00:00 2001 From: Pixelflinger Date: Tue, 14 Apr 2020 15:59:45 -0700 Subject: [PATCH] Implement texture swizzling --- RELEASE_NOTES.md | 1 + .../filament-android/src/main/cpp/Texture.cpp | 8 ++++++ .../com/google/android/filament/Texture.java | 28 +++++++++++++++++++ .../backend/include/backend/DriverEnums.h | 10 +++++++ .../include/private/backend/DriverAPI.inc | 7 +++++ filament/backend/src/metal/MetalDriver.mm | 5 ++++ filament/backend/src/noop/NoopDriver.cpp | 4 +++ filament/backend/src/opengl/GLUtils.h | 18 ++++++++++++ filament/backend/src/opengl/OpenGLDriver.cpp | 13 +++++++++ filament/backend/src/vulkan/VulkanDriver.cpp | 5 ++++ filament/include/filament/Texture.h | 12 ++++++++ filament/src/Texture.cpp | 15 ++++++++++ 12 files changed, 126 insertions(+) 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