| //* 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.cpp' import convert_to_kotlin, jni_signature with context %} |
| {% from 'art/kotlin_record_conversion.cpp' import define_kotlin_record_structure, define_kotlin_to_struct_conversion with context %} |
| #include "structures.h" |
| |
| #include <cassert> |
| #include <string> |
| |
| #include <jni.h> |
| #include <webgpu/webgpu.h> |
| |
| #include "dawn/common/Assert.h" |
| #include "JNIContext.h" |
| |
| // Converts Kotlin objects representing Dawn structures into native structures that can be passed |
| // into the native Dawn API. |
| |
| namespace dawn::kotlin_api { |
| |
| // Helper functions to call the correct JNIEnv::Call*Method depending on what return type we expect. |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jboolean* result) { |
| *result = env->CallBooleanMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jbyte* result) { |
| *result = env->CallByteMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jchar* result) { |
| *result = env->CallCharMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jshort* result) { |
| *result = env->CallShortMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jint* result) { |
| *result = env->CallIntMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jlong* result) { |
| *result = env->CallLongMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jfloat* result) { |
| *result = env->CallFloatMethod(obj, getter); |
| } |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, jdouble* result) { |
| *result = env->CallDoubleMethod(obj, getter); |
| } |
| template <typename T> |
| void CallGetter(JNIEnv* env, jmethodID getter, jobject obj, T** result) { |
| *result = reinterpret_cast<T*>(env->CallObjectMethod(obj, getter)); |
| } |
| |
| // Special-case noop handling of the two callback info that are part of other structures. |
| // TODO(352710628) support converting callback info. |
| void ToNative(JNIContext* c, JNIEnv* env, jobject obj, WGPUDeviceLostCallbackInfo* info) { |
| *info = {}; |
| } |
| |
| void ToNative(JNIContext* c, JNIEnv* env, jobject obj, WGPUUncapturedErrorCallbackInfo* info) { |
| *info = {}; |
| } |
| |
| jobject ToKotlin(JNIEnv *env, const WGPUDeviceLostCallbackInfo* input) { |
| return nullptr; |
| } |
| |
| jobject ToKotlin(JNIEnv *env, const WGPUUncapturedErrorCallbackInfo* input) { |
| return nullptr; |
| } |
| |
| // Special-case [Nullable]StringView |
| void ToNative(JNIContext* c, JNIEnv* env, jstring obj, WGPUStringView* s) { |
| if (obj == nullptr) { |
| *s = {nullptr, WGPU_STRLEN}; |
| return; |
| } |
| *s = {c->GetStringUTFChars(obj), static_cast<size_t>(env->GetStringUTFLength(obj))}; |
| } |
| |
| jobject ToKotlin(JNIEnv* env, const WGPUStringView* s) { |
| if (s->length == WGPU_STRLEN) { |
| if (s->data == nullptr) { |
| return nullptr; |
| } |
| return env->NewStringUTF(s->data); |
| } |
| std::string nullTerminated(s->data, s->length); |
| return env->NewStringUTF(nullTerminated.c_str()); |
| } |
| |
| {%- for structure in by_category['structure'] if include_structure(structure) %} |
| |
| //* Native -> Kotlin converter. |
| //* TODO(b/354411474): Filter the structures for which to add a ToKotlin conversion. |
| jobject ToKotlin(JNIEnv *env, const {{ as_cType(structure.name) }}* input) { |
| if (!input) { |
| return nullptr; |
| } |
| |
| //* Make a new Kotlin object to receive a copy of the structure. |
| jclass clz = env->FindClass("{{ jni_name(structure) }}"); |
| //* JNI signature needs to be built using the same logic used in the Kotlin structure spec. |
| jmethodID ctor = env->GetMethodID(clz, "<init>", "( |
| {%- for member in kotlin_record_members(structure.members) %} |
| {{- jni_signature(member) -}} |
| {%- endfor -%} |
| {%- for structure in chain_children[structure.name.get()] -%} |
| {{- jni_signature({'type': structure}) -}} |
| {%- endfor %})V"); |
| //* Each field converted using the individual value converter. |
| {% for member in kotlin_record_members(structure.members) %} |
| {{ convert_to_kotlin('input->' + member.name.camelCase(), member.name.camelCase(), |
| 'input->' + member.length.name.camelCase() if member.length.name, |
| member) | indent(4) -}} |
| {% endfor %} |
| //* Allow conversion of every child structure. |
| {%- for structure in chain_children[structure.name.get()] %} |
| jobject {{ structure.name.camelCase() }} = nullptr; |
| {% endfor %} |
| //* Walk the chain to find and convert (recursively) all child structures. |
| {%- if chain_children[structure.name.get()] %} |
| for (const WGPUChainedStruct* child = input->nextInChain; |
| child != nullptr; child = child->next) { |
| switch (child->sType) { |
| {% for structure in chain_children[structure.name.get()] %} |
| case WGPUSType_{{ structure.name.CamelCase() }}: |
| {{ structure.name.camelCase() }} = ToKotlin(env, |
| reinterpret_cast<const {{ as_cType(structure.name) }}*>(child)); |
| break; |
| {% endfor %} |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| {% endif %} |
| |
| //* Now all the fields are converted, invoke the constructor. |
| jobject converted = env->NewObject( |
| clz, |
| ctor |
| {%- for member in kotlin_record_members(structure.members) %}, |
| {{ member.name.camelCase() -}} |
| {%- endfor -%} |
| {%- for structure in chain_children[structure.name.get()] -%}, |
| {{ structure.name.camelCase() -}} |
| {%- endfor -%} |
| ); |
| return converted; |
| } |
| |
| {% set Struct = as_cType(structure.name) %} |
| {% set KotlinRecord = "KotlinRecord" + structure.name.CamelCase() %} |
| {{ define_kotlin_record_structure(KotlinRecord, structure.members)}} |
| {{ define_kotlin_to_struct_conversion("ConvertInternal", KotlinRecord, Struct, structure.members)}} |
| void ToNative(JNIContext* c, JNIEnv* env, jobject obj, {{ as_cType(structure.name) }}* converted) { |
| jclass clz = env->FindClass("{{ jni_name(structure) }}"); |
| |
| //* Use getters to fill in the Kotlin record that will get converted to our struct. |
| {{KotlinRecord}} kotlinRecord; |
| {% for member in kotlin_record_members(structure.members) %} |
| { |
| jmethodID getter = env->GetMethodID(clz, "get{{member.name.CamelCase()}}", "(){{jni_signature(member)}}"); |
| CallGetter(env, getter, obj, &kotlinRecord.{{as_varName(member.name)}}); |
| } |
| {% endfor %} |
| |
| //* Fill all struct members from the Kotlin record. |
| ConvertInternal(c, kotlinRecord, converted); |
| //* Set up the chain type and links for child objects. |
| {% if structure.chained %} |
| converted->chain = {.sType = WGPUSType_{{ structure.name.CamelCase() }}}; |
| {% endif %} |
| {% for child in chain_children[structure.name.get()] %} |
| { |
| jobject child = env->CallObjectMethod(obj, |
| env->GetMethodID(clz, "get{{ child.name.CamelCase() }}", "()L{{ jni_name(child) }};")); |
| if (child) { |
| auto out = c->Alloc<{{ as_cType(child.name) }}>(); |
| ToNative(c, env, child, out); |
| out->chain.next = converted->nextInChain; |
| converted->nextInChain = &out->chain; |
| } |
| } |
| {% endfor %} |
| } |
| {% endfor %} |
| |
| } // namespace dawn::kotlin_api |