| //* Copyright 2024 The Dawn & Tint Authors | |
| //* | |
| //* Redistribution and use in source and binary forms, with or without | |
| //* modification, are permitted provided that the following conditions are met: | |
| //* | |
| //* 1. Redistributions of source code must retain the above copyright notice, this | |
| //* list of conditions and the following disclaimer. | |
| //* | |
| //* 2. Redistributions in binary form must reproduce the above copyright notice, | |
| //* this list of conditions and the following disclaimer in the documentation | |
| //* and/or other materials provided with the distribution. | |
| //* | |
| //* 3. Neither the name of the copyright holder nor the names of its | |
| //* contributors may be used to endorse or promote products derived from | |
| //* this software without specific prior written permission. | |
| //* | |
| //* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| //* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| //* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| //* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| //* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| //* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| //* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| //* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| //* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| //* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| {% from 'art/api_jni_types.kt' import arg_to_jni_type, convert_to_kotlin, jni_signature with context %} | |
| {% macro define_kotlin_record_structure(struct_name, members) %} | |
| struct {{struct_name}} { | |
| {% for member in kotlin_record_members(members) %} | |
| //* HACK: Hardcode that ANativeWindow is a jlong instead of an actual pointer. Instead | |
| //* of this, we should have manually written method that directly creates the | |
| //* wgpu::Surface from the Java Surface. | |
| {% if member.name.get() == "window" and member.type.name.get() == "void *"%} | |
| jlong {{ as_varName(member.name) }}; | |
| {% else %} | |
| {{ arg_to_jni_type(member) }} {{ as_varName(member.name) }}; | |
| {% endif %} | |
| {% endfor%} | |
| }; | |
| {% endmacro %} | |
| {% macro define_kotlin_to_struct_conversion(function_name, kotlin_name, struct_name, members) %} | |
| inline void {{function_name}}(JNIContext* c, const {{kotlin_name}}& inStruct, {{struct_name}}* outStruct) { | |
| JNIEnv* env = c->env; | |
| *outStruct = {}; | |
| {% for member in kotlin_record_members(members) %} { | |
| auto& in = inStruct.{{member.name.camelCase()}}; | |
| auto& out = outStruct->{{member.name.camelCase()}}; | |
| {% if member.length == 'strlen' %} | |
| if (in != nullptr) { | |
| out = c->GetStringUTFChars(in); | |
| } else { | |
| out = nullptr; | |
| } | |
| {% elif member.constant_length == 1 %} | |
| {% if member.type.category == 'structure' %} | |
| if (in != nullptr) { | |
| auto converted = c->Alloc<{{as_cType(member.type.name)}}>(); | |
| ToNative(c, env, in, converted); | |
| out = converted; | |
| } | |
| {% elif member.type.name.get() == 'void' %} | |
| out = reinterpret_cast<void*>(in); | |
| {% else %} | |
| {{ unreachable_code() }} | |
| {% endif %} | |
| {% elif member.length %} | |
| auto& outLength = outStruct->{{member.length.name.camelCase()}}; | |
| {% if member.constant_length %} | |
| {{ unreachable_code() }} | |
| {% endif %} | |
| //* Convert container, including the length field. | |
| {% if member.type.name.get() == 'uint32_t' %} | |
| //* This container type is represented in Kotlin as a primitive array. | |
| out = reinterpret_cast<const uint32_t*>(c->GetIntArrayElements(in)); | |
| outLength = env->GetArrayLength(in); | |
| {% elif member.type.name.get() == 'void' %} | |
| out = env->GetDirectBufferAddress(in); | |
| outLength = env->GetDirectBufferCapacity(in); | |
| {% else %} | |
| //* These container types are represented in Kotlin as arrays of objects. | |
| outLength = env->GetArrayLength(in); | |
| auto array = c->AllocArray<{{ as_cType(member.type.name) }}>(outLength); | |
| out = array; | |
| {% if member.type.category in ['bitmask', 'enum'] %} | |
| jclass memberClass = env->FindClass("{{ jni_name(member.type) }}"); | |
| jmethodID getValue = env->GetMethodID(memberClass, "getValue", "()I"); | |
| for (int idx = 0; idx != outLength; idx++) { | |
| jobject element = env->GetObjectArrayElement(in, idx); | |
| array[idx] = static_cast<{{ as_cType(member.type.name) }}>( | |
| env->CallIntMethod(element, getValue)); | |
| } | |
| {% elif member.type.category == 'object' %} | |
| jclass memberClass = env->FindClass("{{ jni_name(member.type) }}"); | |
| jmethodID getHandle = env->GetMethodID(memberClass, "getHandle", "()J"); | |
| for (int idx = 0; idx != outLength; idx++) { | |
| jobject element = env->GetObjectArrayElement(in, idx); | |
| array[idx] = reinterpret_cast<{{ as_cType(member.type.name) }}>( | |
| env->CallLongMethod(element, getHandle)); | |
| } | |
| {% elif member.type.category == 'structure' %} | |
| for (int idx = 0; idx != outLength; idx++) { | |
| ToNative(c, env, env->GetObjectArrayElement(in, idx), &array[idx]); | |
| } | |
| {% else %} | |
| {{ unreachable_code() }} | |
| {% endif %} | |
| {% endif %} | |
| //* From here members are single values. | |
| {% elif member.type.category == 'object' %} | |
| if (in != nullptr) { | |
| jclass memberClass = env->FindClass("{{ jni_name(member.type) }}"); | |
| jmethodID getHandle = env->GetMethodID(memberClass, "getHandle", "()J"); | |
| out = reinterpret_cast<{{as_cType(member.type.name)}}>( | |
| env->CallLongMethod(in, getHandle)); | |
| } else { | |
| out = nullptr; | |
| } | |
| {% elif member.type.category == 'structure' %} | |
| //* Mandatory structure. | |
| ToNative(c, env, in, &out); | |
| {% elif member.name.get() == "window" and member.type.name.get() == "void *" %} | |
| //* HACK: Hardcode that ANativeWindow is a jlong instead of an actual pointer. Instead | |
| //* of this, we should have manually written method that directly creates the | |
| //* wgpu::Surface from the Java Surface. | |
| out = reinterpret_cast<{{as_cType(member.type.name)}}>(static_cast<uintptr_t>(in)); | |
| {% elif member.type.category in ["native", "enum", "bitmask"] %} | |
| out = static_cast<{{as_cType(member.type.name)}}>(in); | |
| {% elif member.type.category == 'function pointer' %} | |
| //* Function pointers themselves require each argument converting. | |
| //* A custom native callback is generated to wrap the Kotlin callback. | |
| out = []( | |
| {%- for callbackArg in member.type.arguments %} | |
| {{ as_annotated_cType(callbackArg) }}{{ ',' if not loop.last }} | |
| {%- endfor %}) { | |
| UserData* userData1 = static_cast<UserData *>(userdata); | |
| JNIEnv *env = userData1->env; | |
| if (env->ExceptionCheck()) { | |
| return; | |
| } | |
| {%- for callbackArg in kotlin_record_members(member.type.arguments) -%} | |
| {{ convert_to_kotlin(callbackArg.name.camelCase(), | |
| '_' + callbackArg.name.camelCase(), | |
| 'input->' + callbackArg.length.name.camelCase() if callbackArg.length.name, | |
| callbackArg) }} | |
| {% endfor %} | |
| //* Get the client (Kotlin) callback so we can call it. | |
| jmethodID callbackMethod = env->GetMethodID( | |
| env->FindClass("{{ jni_name(member.type) }}"), "callback", "( | |
| {%- for callbackArg in kotlin_record_members(member.type.arguments) -%} | |
| {{- jni_signature(callbackArg) -}} | |
| {%- endfor %})V"); | |
| //* Call the callback with all converted parameters. | |
| env->CallVoidMethod(userData1->callback, callbackMethod | |
| {%- for callbackArg in kotlin_record_members(member.type.arguments) %} | |
| ,_{{ callbackArg.name.camelCase() }} | |
| {%- endfor %}); | |
| }; | |
| //* TODO(b/330293719): free associated resources. | |
| outStruct->userdata = new UserData( | |
| {.env = env, .callback = env->NewGlobalRef(in)}); | |
| {% else %} | |
| {{ unreachable_code() }} | |
| {% endif %} | |
| } {% endfor %} | |
| } | |
| {% endmacro %} |