Files
filament/android/filament-android/src/main/cpp/Texture.cpp
Mathias Agopian 61fb5a588e rework backend texture upload APIs
The main goal is to allow more flexibility, allow cubemap arrays in
the future and better match vk and metal apis.

Main changes:
- remove updateCubeImage
- remove update2DImage
- update3DImage is now the only texture upload backend API

cubemaps are now treated just like a 2D array of 6 layers.

For this reason, Texture::setImage(..., FaceOFfsets) is deprecated.
Additionally, the 2D versions of Texture::setImage() become inline
helpers.

A side effect of this change is that it is now possible to update only
a single face of a cubemap, but also a region of a face (or faces).
2022-07-21 11:12:05 -07:00

567 lines
21 KiB
C++

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <algorithm>
#include <functional>
#ifdef __ANDROID__
#include <android/bitmap.h>
#endif
#include <backend/BufferDescriptor.h>
#include <filament/Engine.h>
#include <filament/Stream.h>
#include <filament/Texture.h>
#include "common/CallbackUtils.h"
#include "common/NioUtils.h"
#include "private/backend/VirtualMachineEnv.h"
using namespace filament;
using namespace backend;
static size_t getTextureDataSize(const Texture *texture,
size_t level, Texture::Format format, Texture::Type type,
size_t stride, size_t height, size_t alignment) {
// Zero stride implies tight row-to-row packing.
stride = stride == 0 ? texture->getWidth(level) : std::max(size_t(1), stride >> level);
height = height == 0 ? texture->getHeight(level) : std::max(size_t(1), height >> level);
return Texture::computeTextureDataSize(format, type, stride, height, alignment);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsTextureFormatSupported(JNIEnv*, jclass,
jlong nativeEngine, jint internalFormat) {
Engine *engine = (Engine *) nativeEngine;
return (jboolean) Texture::isTextureFormatSupported(*engine,
(Texture::InternalFormat) internalFormat);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jclass,
jlong nativeEngine) {
Engine *engine = (Engine *) nativeEngine;
return (jboolean) Texture::isTextureSwizzleSupported(*engine);
}
// Texture::Builder...
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Texture_nCreateBuilder(JNIEnv*, jclass) {
return (jlong) new Texture::Builder();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nDestroyBuilder(JNIEnv*, jclass,
jlong nativeBuilder) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
delete builder;
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderWidth(JNIEnv*, jclass,
jlong nativeBuilder, jint width) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->width((uint32_t) width);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderHeight(JNIEnv*, jclass,
jlong nativeBuilder, jint height) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->height((uint32_t) height);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderDepth(JNIEnv*, jclass,
jlong nativeBuilder, jint depth) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->depth((uint32_t) depth);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderLevels(JNIEnv*, jclass,
jlong nativeBuilder, jint levels) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->levels((uint8_t) levels);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderSampler(JNIEnv*, jclass,
jlong nativeBuilder, jint sampler) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->sampler((Texture::Sampler) sampler);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderFormat(JNIEnv*, jclass,
jlong nativeBuilder, jint format) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->format((Texture::InternalFormat) format);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nBuilderUsage(JNIEnv*, jclass,
jlong nativeBuilder, jint flags) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
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 void JNICALL
Java_com_google_android_filament_Texture_nBuilderImportTexture(JNIEnv*, jclass, jlong nativeBuilder, jlong id) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
builder->import((intptr_t)id);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Texture_nBuilderBuild(JNIEnv*, jclass,
jlong nativeBuilder, jlong nativeEngine) {
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
Engine *engine = (Engine *) nativeEngine;
return (jlong) builder->build(*engine);
}
// Texture...
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetWidth(JNIEnv*, jclass, jlong nativeTexture,
jint level) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getWidth((size_t) level);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetHeight(JNIEnv*, jclass, jlong nativeTexture,
jint level) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getHeight((size_t) level);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetDepth(JNIEnv*, jclass, jlong nativeTexture,
jint level) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getDepth((size_t) level);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetLevels(JNIEnv*, jclass, jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getLevels();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetTarget(JNIEnv*, jclass, jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getTarget();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetInternalFormat(JNIEnv*, jclass,
jlong nativeTexture) {
Texture *texture = (Texture *) nativeTexture;
return (jint) texture->getFormat();
}
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 top, 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) height, (size_t) alignment) * depth;
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) top,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, 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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, 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,
jint left, jint top, jint type, jint alignment, jint stride, jint format,
jintArray faceOffsetsInBytes_,
jobject handler, jobject runnable) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
jint *faceOffsetsInBytes = env->GetIntArrayElements(faceOffsetsInBytes_, nullptr);
Texture::FaceOffsets faceOffsets;
std::copy_n(faceOffsetsInBytes, 6, faceOffsets.offsets);
env->ReleaseIntArrayElements(faceOffsetsInBytes_, faceOffsetsInBytes, JNI_ABORT);
size_t sizeInBytes = 6 * getTextureDataSize(texture, (size_t) level, (Texture::Format) format,
(Texture::Type) type, (size_t) stride, 0, (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) top,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets);
#pragma clang diagnostic pop
return 0;
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jobject storage, jint remaining,
jint left, jint top, jint type, jint alignment,
jint compressedSizeInBytes, jint compressedFormat, jintArray faceOffsetsInBytes_,
jobject handler, jobject runnable) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
jint *faceOffsetsInBytes = env->GetIntArrayElements(faceOffsetsInBytes_, nullptr);
Texture::FaceOffsets faceOffsets;
std::copy_n(faceOffsetsInBytes, 6, faceOffsets.offsets);
env->ReleaseIntArrayElements(faceOffsetsInBytes_, faceOffsetsInBytes, JNI_ABORT);
size_t sizeInBytes = 6 * (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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets);
#pragma clang diagnostic pop
return 0;
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv*, jclass, jlong nativeTexture,
jlong nativeEngine, jlong eglImage) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
texture->setExternalImage(*engine, (void*)eglImage);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv*, jclass,
jlong nativeTexture, jlong nativeEngine, jlong nativeStream) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
Stream *stream = (Stream *) nativeStream;
texture->setExternalStream(*engine, stream);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Texture_nGenerateMipmaps(JNIEnv*, jclass,
jlong nativeTexture, jlong nativeEngine) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
texture->generateMipmaps(*engine);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsStreamValidForTexture(JNIEnv*, jclass,
jlong nativeTexture, jlong) {
Texture* texture = (Texture*) nativeTexture;
return (jboolean) (texture->getTarget() == SamplerType::SAMPLER_EXTERNAL);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGeneratePrefilterMipmap(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint width, jint height,
jobject storage, jint remaining, jint left,
jint top, jint type, jint alignment, jint stride, jint format,
jintArray faceOffsetsInBytes_, jobject handler, jobject runnable, jint sampleCount,
jboolean mirror) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
jint *faceOffsetsInBytes = env->GetIntArrayElements(faceOffsetsInBytes_, nullptr);
Texture::FaceOffsets faceOffsets;
std::copy_n(faceOffsetsInBytes, 6, faceOffsets.offsets);
env->ReleaseIntArrayElements(faceOffsetsInBytes_, faceOffsetsInBytes, JNI_ABORT);
stride = stride ? stride : width;
size_t sizeInBytes = 6 *
Texture::computeTextureDataSize((Texture::Format) format, (Texture::Type) type,
(size_t) stride, (size_t) height, (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) top, (uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
Texture::PrefilterOptions options;
options.sampleCount = sampleCount;
options.mirror = mirror;
texture->generatePrefilterMipmap(*engine, std::move(desc), faceOffsets, &options);
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// ANDROID SPECIFIC BITS
////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __ANDROID__
#define BITMAP_CONFIG_ALPHA_8 0
#define BITMAP_CONFIG_RGB_565 1
#define BITMAP_CONFIG_RGBA_4444 2
#define BITMAP_CONFIG_RGBA_8888 3
#define BITMAP_CONFIG_RGBA_F16 4
#define BITMAP_CONFIG_HARDWARE 5
class AutoBitmap : public JniCallback {
private:
AutoBitmap(JNIEnv* env, jobject bitmap) noexcept
: JniCallback(),
mBitmap(env->NewGlobalRef(bitmap)) {
if (mBitmap) {
AndroidBitmap_getInfo(env, mBitmap, &mInfo);
AndroidBitmap_lockPixels(env, mBitmap, &mData);
}
}
AutoBitmap(JNIEnv* env, jobject bitmap, jobject handler, jobject runnable) noexcept
: JniCallback(env, handler, runnable),
mBitmap(env->NewGlobalRef(bitmap)) {
if (mBitmap) {
AndroidBitmap_getInfo(env, mBitmap, &mInfo);
AndroidBitmap_lockPixels(env, mBitmap, &mData);
}
}
void release(JNIEnv* env) {
if (mBitmap) {
AndroidBitmap_unlockPixels(env, mBitmap);
env->DeleteGlobalRef(mBitmap);
}
}
~AutoBitmap() override = default;
public:
void* getData() const noexcept {
return mData;
}
size_t getSizeInBytes() const noexcept {
return mInfo.height * mInfo.stride;
}
PixelDataFormat getFormat(int format) const noexcept {
// AndroidBitmapInfo does not capture the HARDWARE and RGBA_F16 formats
// so we switch on the Bitmap.Config values directly
switch (format) {
case BITMAP_CONFIG_ALPHA_8: return PixelDataFormat::ALPHA;
case BITMAP_CONFIG_RGB_565: return PixelDataFormat::RGB;
default: return PixelDataFormat::RGBA;
}
}
PixelDataType getType(int format) const noexcept {
switch (format) {
case BITMAP_CONFIG_RGB_565: return PixelDataType::USHORT_565;
case BITMAP_CONFIG_RGBA_F16: return PixelDataType::HALF;
default: return PixelDataType::UBYTE;
}
}
// create a AutoBitmap
static AutoBitmap* make(JNIEnv* env, jobject bitmap, jobject handler, jobject runnable) {
return new AutoBitmap(env, bitmap, handler, runnable);
}
// execute the callback on the java thread and destroy ourselves
static void invoke(void*, size_t, void* user) {
auto* autoBitmap = reinterpret_cast<AutoBitmap*>(user);
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
releaseCallbackJni(env, autoBitmap->mCallbackUtils, autoBitmap->mHandler, autoBitmap->mCallback);
autoBitmap->release(env);
delete autoBitmap;
}
// create a AutoBitmap without a handler
static AutoBitmap* make(JNIEnv* env, jobject bitmap) {
return new AutoBitmap(env, bitmap);
}
// just destroy ourselves
static void invokeNoCallback(void*, size_t, void* user) {
auto* autoBitmap = reinterpret_cast<AutoBitmap*>(user);
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
autoBitmap->release(env);
delete autoBitmap;
}
private:
void* mData = nullptr;
jobject mBitmap = nullptr;
jobject mHandler = nullptr;
jobject mCallback = nullptr;
AndroidBitmapInfo mInfo{};
};
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_android_TextureHelper_nSetBitmap(JNIEnv* env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jint xoffset, jint yoffset,
jint width, jint height, jobject bitmap, jint format) {
Texture* texture = (Texture*) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
auto* autoBitmap = AutoBitmap::make(env, bitmap);
Texture::PixelBufferDescriptor desc(
autoBitmap->getData(),
autoBitmap->getSizeInBytes(),
autoBitmap->getFormat(format),
autoBitmap->getType(format),
&AutoBitmap::invokeNoCallback, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_android_TextureHelper_nSetBitmapWithCallback(JNIEnv* env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jint xoffset, jint yoffset,
jint width, jint height, jobject bitmap, jint format, jobject handler, jobject runnable) {
Texture* texture = (Texture*) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
auto* autoBitmap = AutoBitmap::make(env, bitmap, handler, runnable);
Texture::PixelBufferDescriptor desc(
autoBitmap->getData(),
autoBitmap->getSizeInBytes(),
autoBitmap->getFormat(format),
autoBitmap->getType(format),
autoBitmap->getHandler(), &AutoBitmap::invoke, autoBitmap);
texture->setImage(*engine, (size_t) level,
(uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height,
std::move(desc));
}
#endif