Add public APIs for texture 3D and texture 2D arrays

Also cleanup all setImage() error reporting. All error checks are
changed from silent to NON_FATAL_ASSERT, which means they'll throw
if exceptions are enabled or do a no-op and log a message otherwise.

Also added more precondition checks.
This commit is contained in:
Pixelflinger
2020-08-12 17:32:46 -07:00
committed by Mathias Agopian
parent acdb8addfd
commit 7852a9b8cb
5 changed files with 339 additions and 24 deletions
@@ -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,
@@ -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 {
/**
* <code>setImage</code> is used to modify a sub-region of the texure from a CPU-buffer.
* <code>setImage</code> is used to modify a sub-region of the texture from a CPU-buffer.
*
* <p>This <code>Texture</code> 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 {
}
}
/**
* <code>setImage</code> is used to modify a sub-region of the 3D texture or 2D texture array
* from a CPU-buffer.
*
* <p>This <code>Texture</code> instance must use {@link Sampler#SAMPLER_2D_ARRAY SAMPLER_2D_ARRAY} or
* {@link Sampler#SAMPLER_3D SAMPLER_3D}.</p>
*
* @param engine {@link Engine} this texture is associated to. Must be the
* instance passed to {@link Builder#build Builder.build()}.
* @param level Level to set the image for. Must be less than {@link #getLevels()}.
* @param xoffset x-offset in texel of the region to modify
* @param yoffset y-offset in texel of the region to modify
* @param yoffset z-offset in texel of the region to modify
* @param width width in texel of the region to modify
* @param height height in texel of the region to modify
* @param depth depth in texel or index of the region to modify
* @param buffer Client-side buffer containing the image to set.
* <code>buffer</code>'s {@link Format format} must match that
* of {@link #getFormat()}
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*
* @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();
}
}
/**
* <code>setImage</code> 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,
+29 -4
View File
@@ -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;
/**
+165 -16
View File
@@ -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);
}
+7 -2
View File
@@ -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,