diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp
index d2a1cdd20b..68ef60a1d7 100644
--- a/android/filament-android/src/main/cpp/Texture.cpp
+++ b/android/filament-android/src/main/cpp/Texture.cpp
@@ -233,6 +233,76 @@ Java_com_google_android_filament_Texture_nSetImageCompressed(JNIEnv *env, jclass
return 0;
}
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_android_filament_Texture_nSetImage3D(JNIEnv* env, jclass, jlong nativeTexture,
+ jlong nativeEngine, jint level,
+ jint xoffset, jint yoffset, jint zoffset,
+ jint width, jint height, jint depth,
+ jobject storage, jint remaining,
+ jint left, jint bottom, jint type, jint alignment,
+ jint stride, jint format,
+ jobject handler, jobject runnable) {
+ Texture* texture = (Texture*) nativeTexture;
+ Engine* engine = (Engine*) nativeEngine;
+
+ size_t sizeInBytes = getTextureDataSize(texture, (size_t) level, (Texture::Format) format,
+ (Texture::Type) type, (size_t) stride, (size_t) alignment);
+
+ AutoBuffer nioBuffer(env, storage, 0);
+ if (sizeInBytes > (size_t(remaining) << nioBuffer.getShift())) {
+ // BufferOverflowException
+ return -1;
+ }
+
+ void *buffer = nioBuffer.getData();
+ auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
+
+ Texture::PixelBufferDescriptor desc(buffer, sizeInBytes, (backend::PixelDataFormat) format,
+ (backend::PixelDataType) type, (uint8_t) alignment, (uint32_t) left, (uint32_t) bottom,
+ (uint32_t) stride, &JniBufferCallback::invoke, callback);
+
+ texture->setImage(*engine, (size_t) level,
+ (uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
+ (uint32_t) width, (uint32_t) height, (uint32_t) depth,
+ std::move(desc));
+
+ return 0;
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_android_filament_Texture_nSetImage3DCompressed(JNIEnv *env, jclass,
+ jlong nativeTexture, jlong nativeEngine, jint level,
+ jint xoffset, jint yoffset, jint zoffset,
+ jint width, jint height, jint depth,
+ jobject storage, jint remaining,
+ jint, jint, jint, jint, jint compressedSizeInBytes, jint compressedFormat,
+ jobject handler, jobject runnable) {
+ Texture *texture = (Texture *) nativeTexture;
+ Engine *engine = (Engine *) nativeEngine;
+
+ size_t sizeInBytes = (size_t) compressedSizeInBytes;
+
+ AutoBuffer nioBuffer(env, storage, 0);
+ if (sizeInBytes > (size_t(remaining) << nioBuffer.getShift())) {
+ // BufferOverflowException
+ return -1;
+ }
+
+ void *buffer = nioBuffer.getData();
+ auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
+
+ Texture::PixelBufferDescriptor desc(buffer, sizeInBytes,
+ (backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes,
+ &JniBufferCallback::invoke, callback);
+
+ texture->setImage(*engine, (size_t) level,
+ (uint32_t) xoffset, (uint32_t) yoffset, (uint32_t) zoffset,
+ (uint32_t) width, (uint32_t) height, (uint32_t) depth,
+ std::move(desc));
+
+ return 0;
+}
+
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jobject storage, jint remaining,
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 66daba87a7..450f0a2029 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
@@ -92,7 +92,9 @@ public class Texture {
/** Cubemap sampler */
SAMPLER_CUBEMAP,
/** External texture sampler */
- SAMPLER_EXTERNAL
+ SAMPLER_EXTERNAL,
+ /** 3D sampler */
+ SAMPLER_3D,
}
/**
@@ -817,7 +819,7 @@ public class Texture {
/**
- * setImage is used to modify a sub-region of the texure from a CPU-buffer.
+ * setImage is used to modify a sub-region of the texture from a CPU-buffer.
*
*
This Texture instance must use {@link Sampler#SAMPLER_2D SAMPLER_2D} or
* {@link Sampler#SAMPLER_EXTERNAL SAMPLER_EXTERNAL}. If the later is specified
@@ -868,6 +870,58 @@ public class Texture {
}
}
+ /**
+ * setImage is used to modify a sub-region of the 3D texture or 2D texture array
+ * from a CPU-buffer.
+ *
+ *
This Texture instance must use {@link Sampler#SAMPLER_2D_ARRAY SAMPLER_2D_ARRAY} or
+ * {@link Sampler#SAMPLER_3D SAMPLER_3D}.
buffer's {@link Format format} must match that
+ * of {@link #getFormat()}
+ *
+ * @exception BufferOverflowException if the specified parameters would result in reading
+ * outside of buffer.
+ *
+ * @see Builder#sampler
+ * @see PixelBufferDescriptor
+ */
+ public void setImage(@NonNull Engine engine,
+ @IntRange(from = 0) int level,
+ @IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset, @IntRange(from = 0) int zoffset,
+ @IntRange(from = 0) int width, @IntRange(from = 0) int height, @IntRange(from = 0) int depth,
+ @NonNull PixelBufferDescriptor buffer) {
+ int result;
+ if (buffer.type == COMPRESSED) {
+ result = nSetImage3DCompressed(getNativeObject(), engine.getNativeObject(), level,
+ xoffset, yoffset, zoffset, width, height, depth,
+ buffer.storage, buffer.storage.remaining(),
+ buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment,
+ buffer.compressedSizeInBytes, buffer.compressedFormat.ordinal(),
+ buffer.handler, buffer.callback);
+ } else {
+ result = nSetImage3D(getNativeObject(), engine.getNativeObject(), level,
+ xoffset, yoffset, zoffset, width, height, depth,
+ buffer.storage, buffer.storage.remaining(),
+ buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment,
+ buffer.stride, buffer.format.ordinal(),
+ buffer.handler, buffer.callback);
+ }
+ if (result < 0) {
+ throw new BufferOverflowException();
+ }
+ }
+
/**
* setImage is used to specify all six images of a cubemap level and
* follows exactly the OpenGL conventions
@@ -1116,6 +1170,18 @@ public class Texture {
int compressedSizeInBytes, int compressedFormat,
Object handler, Runnable callback);
+ private static native int nSetImage3D(long nativeTexture, long nativeEngine,
+ int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth,
+ Buffer storage, int remaining, int left, int bottom, int type, int alignment,
+ int stride, int format,
+ Object handler, Runnable callback);
+
+ private static native int nSetImage3DCompressed(long nativeTexture, long nativeEngine,
+ int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth,
+ Buffer storage, int remaining, int left, int bottom, int type, int alignment,
+ int compressedSizeInBytes, int compressedFormat,
+ Object handler, Runnable callback);
+
private static native int nSetImageCubemap(long nativeTexture, long nativeEngine,
int level, Buffer storage, int remaining, int left, int bottom, int type,
int alignment, int stride, int format,
diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h
index b51a4187e0..afca19800c 100644
--- a/filament/include/filament/Texture.h
+++ b/filament/include/filament/Texture.h
@@ -129,7 +129,7 @@ public:
* effectively create a 3D texture.
* @param depth Depth of the texture in texels (default: 1).
* @return This Builder, for chaining calls.
- * @attention This Texture instance must use Sampler::SAMPLER_2D_ARRAY or it has no effect.
+ * @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_ARRAY or it has no effect.
*/
Builder& depth(uint32_t depth) noexcept;
@@ -305,7 +305,7 @@ public:
*
* @see Builder::sampler()
*/
- void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const noexcept;
+ void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 2D texture for a level.
@@ -331,7 +331,32 @@ public:
*/
void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
- PixelBufferDescriptor&& buffer) const noexcept;
+ PixelBufferDescriptor&& buffer) const;
+
+ /**
+ * Updates a sub-image of a 3D texture or 2D texture array for a level.
+ *
+ * @param engine Engine this texture is associated to.
+ * @param level Level to set the image for.
+ * @param xoffset Left offset of the sub-region to update.
+ * @param yoffset Bottom offset of the sub-region to update.
+ * @param zoffset Depth offset of the sub-region to update.
+ * @param width Width of the sub-region to update.
+ * @param height Height of the sub-region to update.
+ * @param depth Depth of the sub-region to update.
+ * @param buffer Client-side buffer containing the image to set.
+ *
+ * @attention \p engine must be the instance passed to Builder::build()
+ * @attention \p level must be less than getLevels().
+ * @attention \p buffer's Texture::Format must match that of getFormat().
+ * @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_array.
+ *
+ * @see Builder::sampler()
+ */
+ void setImage(Engine& engine, size_t level,
+ uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
+ uint32_t width, uint32_t height, uint32_t depth,
+ PixelBufferDescriptor&& buffer) const;
/**
* Specify all six images of a cube map level.
@@ -352,7 +377,7 @@ public:
* @see Texture::CubemapFace, Builder::sampler()
*/
void setImage(Engine& engine, size_t level,
- PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const noexcept;
+ PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
/**
diff --git a/filament/src/Texture.cpp b/filament/src/Texture.cpp
index 023c35d1a4..f535db4fef 100644
--- a/filament/src/Texture.cpp
+++ b/filament/src/Texture.cpp
@@ -179,7 +179,19 @@ size_t FTexture::getDepth(size_t level) const noexcept {
void FTexture::setImage(FEngine& engine,
size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
- Texture::PixelBufferDescriptor&& buffer) const noexcept {
+ Texture::PixelBufferDescriptor&& buffer) const {
+
+ auto validateTarget = [](SamplerType sampler) -> bool {
+ switch (sampler) {
+ case SamplerType::SAMPLER_2D:
+ case SamplerType::SAMPLER_EXTERNAL:
+ return true;
+ case SamplerType::SAMPLER_CUBEMAP:
+ case SamplerType::SAMPLER_3D:
+ case SamplerType::SAMPLER_2D_ARRAY:
+ return false;
+ }
+ };
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.type == PixelDataType::COMPRESSED ||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
@@ -188,16 +200,130 @@ void FTexture::setImage(FEngine& engine,
return;
}
- if (!mStream && mTarget != Sampler::SAMPLER_CUBEMAP && level < mLevelCount) {
- if (buffer.buffer) {
- engine.getDriverApi().update2DImage(mHandle,
- uint8_t(level), xoffset, yoffset, width, height, std::move(buffer));
- }
+ if (!ASSERT_POSTCONDITION_NON_FATAL(!mStream, "setImage() called on a Stream texture.")) {
+ return;
}
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(level < mLevelCount,
+ "level=%u is >= to levelCount=%u.", unsigned(level), unsigned(mLevelCount))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(validateTarget(mTarget),
+ "Texture Sampler type (%u) not supported for this operation.", unsigned(mTarget))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.buffer, "Data buffer is nullptr.")) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(mSampleCount <= 1,
+ "Operation not supported with multisample (%u) texture.", unsigned(mSampleCount))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(xoffset + width <= valueForLevel(level, mWidth),
+ "xoffset (%u) + width (%u) > texture width (%u) at level (%u)",
+ unsigned(xoffset), unsigned(width), unsigned(valueForLevel(level, mWidth)), unsigned(level))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(yoffset + height <= valueForLevel(level, mHeight),
+ "xoffset (%u) + width (%u) > texture width (%u) at level (%u)",
+ unsigned(yoffset), unsigned(height), unsigned(valueForLevel(level, mHeight)), unsigned(level))) {
+ return;
+ }
+
+ engine.getDriverApi().update2DImage(mHandle,
+ uint8_t(level), xoffset, yoffset, width, height, std::move(buffer));
+}
+
+void FTexture::setImage(FEngine& engine,
+ size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
+ uint32_t width, uint32_t height, uint32_t depth,
+ Texture::PixelBufferDescriptor&& buffer) const {
+
+ auto validateTarget = [](SamplerType sampler) -> bool {
+ switch (sampler) {
+ case SamplerType::SAMPLER_3D:
+ case SamplerType::SAMPLER_2D_ARRAY:
+ return true;
+ case SamplerType::SAMPLER_2D:
+ case SamplerType::SAMPLER_EXTERNAL:
+ case SamplerType::SAMPLER_CUBEMAP:
+ return false;
+ }
+ };
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.type == PixelDataType::COMPRESSED ||
+ validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
+ "The combination of internal format=%u and {format=%u, type=%u} is not supported.",
+ unsigned(mFormat), unsigned(buffer.format), unsigned(buffer.type))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(!mStream, "setImage() called on a Stream texture.")) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(level < mLevelCount,
+ "level=%u is >= to levelCount=%u.", unsigned(level), unsigned(mLevelCount))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(validateTarget(mTarget),
+ "Texture Sampler type (%u) not supported for this operation.", unsigned(mTarget))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(mSampleCount <= 1,
+ "Operation not supported with multisample (%u) texture.", unsigned(mSampleCount))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(xoffset + width <= valueForLevel(level, mWidth),
+ "xoffset (%u) + width (%u) > texture width (%u) at level (%u)",
+ unsigned(xoffset), unsigned(width), unsigned(valueForLevel(level, mWidth)), unsigned(level))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(yoffset + height <= valueForLevel(level, mHeight),
+ "yoffset (%u) + height (%u) > texture height (%u) at level (%u)",
+ unsigned(yoffset), unsigned(height), unsigned(valueForLevel(level, mHeight)), unsigned(level))) {
+ return;
+ }
+
+ // effective level is just how we compute the index/depth based on whether we're an array or a 3D texture
+ const uint8_t effectiveLevel = mTarget == SamplerType::SAMPLER_3D ? level : 0;
+ if (!ASSERT_POSTCONDITION_NON_FATAL(zoffset + depth <= valueForLevel(effectiveLevel, mDepth),
+ "zoffset (%u) + depth (%u) > texture depth (%u) at level (%u)",
+ unsigned(zoffset), unsigned(depth), unsigned(valueForLevel(effectiveLevel, mDepth)), unsigned(level))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.buffer, "Data buffer is nullptr.")) {
+ return;
+ }
+
+ engine.getDriverApi().update3DImage(mHandle,
+ uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(buffer));
}
void FTexture::setImage(FEngine& engine, size_t level,
- Texture::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const noexcept {
+ Texture::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const {
+
+ auto validateTarget = [](SamplerType sampler) -> bool {
+ switch (sampler) {
+ case SamplerType::SAMPLER_CUBEMAP:
+ return true;
+ case SamplerType::SAMPLER_3D:
+ case SamplerType::SAMPLER_2D_ARRAY:
+ case SamplerType::SAMPLER_2D:
+ case SamplerType::SAMPLER_EXTERNAL:
+ return false;
+ }
+ };
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.type == PixelDataType::COMPRESSED ||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
@@ -206,12 +332,26 @@ void FTexture::setImage(FEngine& engine, size_t level,
return;
}
- if (!mStream && mTarget == Sampler::SAMPLER_CUBEMAP && level < mLevelCount) {
- if (buffer.buffer) {
- engine.getDriverApi().updateCubeImage(mHandle, uint8_t(level),
- std::move(buffer), faceOffsets);
- }
+ if (!ASSERT_POSTCONDITION_NON_FATAL(!mStream, "setImage() called on a Stream texture.")) {
+ return;
}
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(level < mLevelCount,
+ "level=%u is >= to levelCount=%u.", unsigned(level), unsigned(mLevelCount))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(validateTarget(mTarget),
+ "Texture Sampler type (%u) not supported for this operation.", unsigned(mTarget))) {
+ return;
+ }
+
+ if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.buffer, "Data buffer is nullptr.")) {
+ return;
+ }
+
+ engine.getDriverApi().updateCubeImage(mHandle, uint8_t(level),
+ std::move(buffer), faceOffsets);
}
void FTexture::setExternalImage(FEngine& engine, void* image) noexcept {
@@ -904,20 +1044,29 @@ Texture::InternalFormat Texture::getFormat() const noexcept {
}
void Texture::setImage(Engine& engine, size_t level,
- Texture::PixelBufferDescriptor&& buffer) const noexcept {
+ Texture::PixelBufferDescriptor&& buffer) const {
upcast(this)->setImage(upcast(engine),
- level, 0, 0, uint32_t(getWidth(level)), uint32_t(getHeight(level)), std::move(buffer));
+ level, 0, 0,
+ uint32_t(getWidth(level)), uint32_t(getHeight(level)), std::move(buffer));
}
void Texture::setImage(Engine& engine,
size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
- PixelBufferDescriptor&& buffer) const noexcept {
+ PixelBufferDescriptor&& buffer) const {
upcast(this)->setImage(upcast(engine),
level, xoffset, yoffset, width, height, std::move(buffer));
}
void Texture::setImage(Engine& engine, size_t level,
- Texture::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const noexcept {
+ uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
+ uint32_t width, uint32_t height, uint32_t depth,
+ PixelBufferDescriptor&& buffer) const {
+ upcast(this)->setImage(upcast(engine),
+ level, xoffset, yoffset, zoffset, width, height, depth, std::move(buffer));
+}
+
+void Texture::setImage(Engine& engine, size_t level,
+ Texture::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const {
upcast(this)->setImage(upcast(engine), level, std::move(buffer), faceOffsets);
}
diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h
index a5a01a9abd..1e9f6e9005 100644
--- a/filament/src/details/Texture.h
+++ b/filament/src/details/Texture.h
@@ -50,10 +50,15 @@ public:
void setImage(FEngine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
- PixelBufferDescriptor&& buffer) const noexcept;
+ PixelBufferDescriptor&& buffer) const;
void setImage(FEngine& engine, size_t level,
- PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const noexcept;
+ uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
+ uint32_t width, uint32_t height, uint32_t depth,
+ PixelBufferDescriptor&& buffer) const;
+
+ void setImage(FEngine& engine, size_t level,
+ PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
void generatePrefilterMipmap(FEngine& engine,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,