blob: 28480185a97c3408ddfcaaeaaf42c11d1779145e [file] [log] [blame]
//* 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