Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GL upload function to Java interface #959

Merged
merged 7 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 112 additions & 1 deletion interface/java_binding/src/main/cpp/KtxTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_khronos_ktx_KtxTexture_getDataSize(J
return static_cast<jlong>(ktxTexture_GetDataSize(texture));
}

extern "C" JNIEXPORT jlong JNICALL Java_org_khronos_KTXTexture_getDataSizeUncompressed(JNIEnv *env, jobject thiz)
extern "C" JNIEXPORT jlong JNICALL Java_org_khronos_ktx_KtxTexture_getDataSizeUncompressed(JNIEnv *env, jobject thiz)
{
ktxTexture *texture = get_ktx_texture(env, thiz);
if (texture == NULL)
Expand All @@ -199,6 +199,117 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_khronos_KTXTexture_getDataSizeUncomp
return ktxTexture_GetDataSizeUncompressed(texture);
}


extern "C" JNIEXPORT jint JNICALL Java_org_khronos_ktx_KtxTexture_glUpload(JNIEnv *env, jobject thiz, jintArray javaTexture, jintArray javaTarget, jintArray javaGlError)
{
ktxTexture *texture = get_ktx_texture(env, thiz);
if (texture == NULL)
{
ThrowDestroyed(env);
return 0;
}

// The target array may not be NULL, and must have
// a size of at least 1
if (javaTarget == NULL)
{
ThrowByName(env, "java/lang/NullPointerException", "Parameter 'target' is null for glUpload");
return 0;
}
jsize javaTargetSize = env->GetArrayLength(javaTarget);
if (javaTargetSize == 0)
{
ThrowByName(env, "java/lang/IllegalArgumentException", "Parameter 'target' may not have length 0");
return 0;
}

// The texture array may be NULL, but if it is not NULL,
// then it must have a length of at least 1
if (javaTexture != NULL)
{
jsize javaTextureSize = env->GetArrayLength(javaTexture);
if (javaTextureSize == 0)
{
ThrowByName(env, "java/lang/IllegalArgumentException", "Parameter 'texture' may not have length 0");
return 0;
}
}

// The GL error array may be NULL, but if it is not NULL,
// then it must have a length of at least 1
if (javaGlError != NULL)
{
jsize javaGlErrorSize = env->GetArrayLength(javaGlError);
if (javaGlErrorSize == 0)
{
ThrowByName(env, "java/lang/IllegalArgumentException", "Parameter 'glError' may not have length 0");
return 0;
}
}

GLuint textureValue = 0;
GLuint *pTexture = &textureValue;
if (javaTexture != NULL)
{
jint *javaTextureArrayElements = env->GetIntArrayElements(javaTexture, NULL);
if (javaTextureArrayElements == NULL)
{
// OutOfMemoryError is already pending
return 0;
}
textureValue = static_cast<GLuint>(javaTextureArrayElements[0]);
env->ReleaseIntArrayElements(javaTexture, javaTextureArrayElements, JNI_ABORT);
}

GLenum target;
GLenum glError;

KTX_error_code result = ktxTexture_GLUpload(texture, &textureValue, &target, &glError);

// Write back the texture into the array
if (javaTexture != NULL)
{
jint *javaTextureArrayElements = env->GetIntArrayElements(javaTexture, NULL);
if (javaTextureArrayElements == NULL)
{
// OutOfMemoryError is already pending
return 0;
}
javaTextureArrayElements[0] = static_cast<jint>(textureValue);
env->ReleaseIntArrayElements(javaTexture, javaTextureArrayElements, JNI_COMMIT);
}

// Write back the target into the array
if (javaTarget != NULL)
{
jint *javaTargetArrayElements = env->GetIntArrayElements(javaTarget, NULL);
if (javaTargetArrayElements == NULL)
{
// OutOfMemoryError is already pending
return 0;
}
javaTargetArrayElements[0] = static_cast<jint>(target);
env->ReleaseIntArrayElements(javaTarget, javaTargetArrayElements, JNI_COMMIT);
}

// Write back the error into the array
if (javaGlError != NULL)
{
jint *javaGlErrorArrayElements = env->GetIntArrayElements(javaGlError, NULL);
if (javaGlErrorArrayElements == NULL)
{
// OutOfMemoryError is already pending
return 0;
}
javaGlErrorArrayElements[0] = static_cast<jint>(glError);
env->ReleaseIntArrayElements(javaGlError, javaGlErrorArrayElements, JNI_COMMIT);
}
return result;
}




extern "C" JNIEXPORT jint JNICALL Java_org_khronos_ktx_KtxTexture_getElementSize(JNIEnv *env, jobject thiz)
{
ktxTexture *texture = get_ktx_texture(env, thiz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,61 @@ public boolean isDestroyed() {
*/
public native long getImageOffset(int level, int layer, int faceSlice);

/**
* Create a GL texture object from a {@link KtxTexture} object.<br>
* <br>
* This may only be called when a GL context is current.<br>
* <br>
* In order to ensure that the GL uploader is not linked into an application
* unless explicitly called, this is not a virtual function. It determines
* the texture type then dispatches to the correct function.<br>
* <br>
* Sets the texture object's <code>GL_TEXTURE_MAX_LEVEL</code> parameter
* according to the number of levels in the KTX data, provided the
* context supports this feature.<br>
* <br>
* Unpacks compressed {@link KtxInternalformat#GL_ETC1_RGB8_OES} and
* <code>GL_ETC2_*</code> format textures in software when the format
* is not supported by the GL context, provided the library has been
* compiled with <code>SUPPORT_SOFTWARE_ETC_UNPACK</code> defined as 1.
*
* It will also convert textures with legacy formats to their modern
* equivalents when the format is not supported by the GL context,
* provided the library has been compiled with
* <code>SUPPORT_LEGACY_FORMAT_CONVERSION</code> defined as 1.
*
* @param texture An array that is either <code>null</code>, or
* has a length of at least 1. It contains the name of the GL texture
* object to load. If it is <code>null</code> or contains 0, the
* function will generate a texture name. The function binds either
* the generated name or the name given in <code>texture</code> to
* the texture target returned in <code>target</code>, before
* loading the texture data. If pTexture is not <code>null</code>
* and a name was generated, the generated name will be returned
* in <code>texture</code>.
* @param target An array with a length of at least 1, where
* element 0 will receive the GL target value
* @param glError An array with a length of at least 1, where
* element 0 will receive any GL error information
* @return {@link KtxErrorCode#SUCCESS} on sucess.
* Returns {@link KtxErrorCode#GL_ERROR} when GL error was raised by
* <code>glBindTexture</code>, <code>glGenTextures</code> or
* <code>gl*TexImage*</code> The GL error will be returned in
* <code>glError</code> if <code>glError</code> is not
* <code>null</code>. Returns {@link KtxErrorCode#INVALID_VALUE}
* when target is <code>null</code> or the size of a mip level
* is greater than the size of the preceding level. Returns
* {@link KtxErrorCode#NOT_FOUND} when a dynamically loaded
* OpenGL function required by the loader was not found.
* Returns {@link KtxErrorCode#UNSUPPORTED_TEXTURE_TYPE} when
* the type of texture is not supported by the current OpenGL context.
* @throws NullPointerException If the given <code>target</code>
* array is <code>null</code>
* @throws IllegalArgumentException Any array that is not
* <code>null</code> has a length of 0
*/
public native int glUpload(int texture[], int target[], int glError[]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious. Why are these parameters arrays?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's mentioned in the first post. And it is the most important "design decision" here. I'll elaborate that in a comment below.


/**
* Destroy the KTX texture and free memory image resources.<br>
* <br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -599,4 +599,92 @@ public void testSupercompressionZLIB() throws IOException {

t.destroy();
}

@Test
public void testBindings() {
Path testKtxFile = Paths.get("")
.resolve("../../tests/testimages/astc_ldr_4x4_FlightHelmet_baseColor.ktx2")
.toAbsolutePath()
.normalize();

KtxTexture2 texture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
KtxTextureCreateFlagBits.NO_FLAGS);
texture.getOETF();
texture.getPremultipliedAlpha();
texture.needsTranscoding();
texture.getSupercompressionScheme();
texture.getVkFormat();

texture.isArray();
texture.isCubemap();
texture.isCompressed();
texture.getGenerateMipmaps();
texture.getBaseWidth();
texture.getBaseHeight();
texture.getBaseDepth();
texture.getNumDimensions();
texture.getNumLevels();
texture.getNumFaces();
texture.getDataSize();
texture.getDataSizeUncompressed();
texture.getElementSize();
texture.getRowPitch(0);
texture.getImageSize(0);
}

@Test
public void testGlUpload() {
Path testKtxFile = Paths.get("")
.resolve("../../tests/testimages/astc_ldr_4x4_FlightHelmet_baseColor.ktx2")
.toAbsolutePath()
.normalize();

KtxTexture2 ktxTexture = KtxTexture2.createFromNamedFile(testKtxFile.toString(),
KtxTextureCreateFlagBits.NO_FLAGS);
ktxTexture.transcodeBasis(KtxTranscodeFormat.BC1_RGB, 0);

int texture0[] = { };
int target0[] = { };
int glError0[] = { };
int texture1[] = { 0 };
int target1[] = { 0 };
int glError1[] = { 0 };

// Expect NullPointerException when target is null
assertThrows(NullPointerException.class,
() -> {
ktxTexture.glUpload(texture1, null, glError1);
},
"Expected to throw NullPointerException");

// Expect IllegalArgumentException when texture length is 0
assertThrows(IllegalArgumentException.class,
() -> {
ktxTexture.glUpload(texture0, target1, glError1);
},
"Expected to throw NullPointerException");

// Expect IllegalArgumentException when target length is 0
assertThrows(IllegalArgumentException.class,
() -> {
ktxTexture.glUpload(texture1, target0, glError1);
},
"Expected to throw NullPointerException");

// Expect IllegalArgumentException when glError length is 0
assertThrows(IllegalArgumentException.class,
() -> {
ktxTexture.glUpload(texture1, target1, glError0);
},
"Expected to throw NullPointerException");

// Expect no exceptions when only target is not null
ktxTexture.glUpload(null, target1, null);

// Expect no exceptions when all arrays have proper length
ktxTexture.glUpload(texture1, target1, glError1);

ktxTexture.destroy();
}

}
Loading