Расширенный рендерскрипт,Расширенный рендерскрипт

Поскольку приложения, использующие RenderScript, по-прежнему работают внутри виртуальной машины Android, у вас есть доступ ко всем API-интерфейсам платформы, с которыми вы знакомы, но вы можете использовать RenderScript, когда это необходимо. Чтобы облегчить взаимодействие между платформой и средой выполнения RenderScript, также присутствует промежуточный уровень кода, облегчающий взаимодействие и управление памятью между двумя уровнями кода. В этом документе более подробно рассматриваются эти различные уровни кода, а также то, как память распределяется между виртуальной машиной Android и средой выполнения RenderScript.

Уровень выполнения RenderScript

Ваш код RenderScript компилируется и выполняется на компактном и четко определенном уровне времени выполнения. API-интерфейсы среды выполнения RenderScript обеспечивают поддержку интенсивных вычислений, которые являются переносимыми и автоматически масштабируются в соответствии с количеством ядер, доступных на процессоре.

Примечание. Стандартные функции C в NDK должны гарантированно выполняться на ЦП, поэтому RenderScript не может получить доступ к этим библиотекам, поскольку RenderScript предназначен для работы на процессорах разных типов.

Вы определяете свой код RenderScript в файлах .rs и .rsh в каталоге src/ вашего проекта Android. Код компилируется в промежуточный байт-код компилятором llvm , который запускается как часть сборки Android. Когда ваше приложение запускается на устройстве, байт-код затем компилируется (точно в срок) в машинный код другим компилятором llvm , находящимся на устройстве. Машинный код оптимизирован для устройства, а также кэшируется, поэтому последующее использование приложения с поддержкой RenderScript не требует перекомпиляции байт-кода.

Некоторые ключевые особенности библиотек времени выполнения RenderScript включают в себя:

  • Функции запроса выделения памяти
  • Большая коллекция математических функций со скалярными и векторными перегруженными версиями многих распространенных процедур. Доступны такие операции, как сложение, умножение, скалярное произведение и перекрестное произведение, а также функции атомарной арифметики и сравнения.
  • Процедуры преобразования примитивных типов данных и векторов, матричные процедуры, а также процедуры даты и времени.
  • Типы данных и структуры для поддержки системы RenderScript, такие как типы векторов для определения двух-, трех- или четырехвекторов.
  • Функции регистрации

Дополнительную информацию о доступных функциях см. в справочнике по API среды выполнения RenderScript.

Отраженный слой

Отраженный слой — это набор классов, которые генерируют инструменты сборки Android, чтобы обеспечить доступ к среде выполнения RenderScript из платформы Android. Этот уровень также предоставляет методы и конструкторы, которые позволяют вам выделять память и работать с ней для указателей, определенных в вашем коде RenderScript. В следующем списке описаны основные отраженные компоненты:

  • Каждый создаваемый вами файл .rs генерируется в класс с именем project_root/gen/package/name/ScriptC_ renderscript_filename типа ScriptC . Этот файл представляет собой версию .java вашего файла .rs , который вы можете вызвать из платформы Android. Этот класс содержит следующие элементы, отраженные в файле .rs :
    • Нестатические функции
    • Нестатические глобальные переменные RenderScript. Методы доступа генерируются для каждой переменной, поэтому вы можете читать и записывать переменные RenderScript из платформы Android. Если глобальная переменная инициализируется на уровне среды выполнения RenderScript, эти значения используются для инициализации соответствующих значений на уровне платформы Android. Если глобальные переменные помечены как const , метод set не создается. Посмотрите здесь для более подробной информации.

    • Глобальные указатели
  • struct отражается в собственном классе с именем project_root/gen/package/name/ScriptField_struct_name , который расширяет Script.FieldBase . Этот класс представляет собой массив struct , который позволяет выделить память для одного или нескольких экземпляров этой struct .

Функции

Функции отражаются в самом классе скрипта, расположенном в project_root/gen/package/name/ScriptC_renderscript_filename . Например, если вы определите следующую функцию в своем коде RenderScript:

void touch(float x, float y, float pressure, int id) {
    if (id >= 10) {
        return;
    }

    touchPos[id].x = x;
    touchPos[id].y = y;
    touchPressure[id] = pressure;
}

затем генерируется следующий код Java:

public void invoke_touch(float x, float y, float pressure, int id) {
    FieldPacker touch_fp = new FieldPacker(16);
    touch_fp.addF32(x);
    touch_fp.addF32(y);
    touch_fp.addF32(pressure);
    touch_fp.addI32(id);
    invoke(mExportFuncIdx_touch, touch_fp);
}

Функции не могут иметь возвращаемые значения, поскольку система RenderScript спроектирована как асинхронная. Когда код платформы Android вызывает RenderScript, этот вызов ставится в очередь и выполняется, когда это возможно. Это ограничение позволяет системе RenderScript работать без постоянных перебоев и повышает эффективность. Если бы функциям было разрешено иметь возвращаемые значения, вызов блокировался бы до тех пор, пока значение не будет возвращено.

Если вы хотите, чтобы код RenderScript отправлял значение обратно в платформу Android, используйте функцию rsSendToClient() .

Переменные

Переменные поддерживаемых типов отражаются в самом классе скрипта, расположенном в project_root/gen/package/name/ScriptC_renderscript_filename . Для каждой переменной создается набор методов доступа. Например, если вы определите следующую переменную в своем коде RenderScript:

uint32_t unsignedInteger = 1;

затем генерируется следующий код Java:

private long mExportVar_unsignedInteger;
public void set_unsignedInteger(long v){
    mExportVar_unsignedInteger = v;
    setVar(mExportVarIdx_unsignedInteger, v);
}

public long get_unsignedInteger(){
    return mExportVar_unsignedInteger;
}
  

Структуры

Структуры отражаются в отдельных классах, расположенных в <project_root>/gen/com/example/renderscript/ScriptField_struct_name . Этот класс представляет массив struct и позволяет выделять память для указанного количества struct . Например, если вы определите следующую структуру:

typedef struct Point {
    float2 position;
    float size;
} Point_t;

тогда в ScriptField_Point.java генерируется следующий код:

package com.example.android.rs.hellocompute;

import android.renderscript.*;
import android.content.res.Resources;

  /**
  * @hide
  */
public class ScriptField_Point extends android.renderscript.Script.FieldBase {

    static public class Item {
        public static final int sizeof = 12;

        Float2 position;
        float size;

        Item() {
            position = new Float2();
        }
    }

    private Item mItemArray[];
    private FieldPacker mIOBuffer;
    public static Element createElement(RenderScript rs) {
        Element.Builder eb = new Element.Builder(rs);
        eb.add(Element.F32_2(rs), "position");
        eb.add(Element.F32(rs), "size");
        return eb.create();
    }

    public  ScriptField_Point(RenderScript rs, int count) {
        mItemArray = null;
        mIOBuffer = null;
        mElement = createElement(rs);
        init(rs, count);
    }

    public  ScriptField_Point(RenderScript rs, int count, int usages) {
        mItemArray = null;
        mIOBuffer = null;
        mElement = createElement(rs);
        init(rs, count, usages);
    }

    private void copyToArray(Item i, int index) {
        if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count
        */);
        mIOBuffer.reset(index * Item.sizeof);
        mIOBuffer.addF32(i.position);
        mIOBuffer.addF32(i.size);
    }

    public void set(Item i, int index, boolean copyNow) {
        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];
        mItemArray[index] = i;
        if (copyNow)  {
            copyToArray(i, index);
            mAllocation.setFromFieldPacker(index, mIOBuffer);
        }
    }

    public Item get(int index) {
        if (mItemArray == null) return null;
        return mItemArray[index];
    }

    public void set_position(int index, Float2 v, boolean copyNow) {
        if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */);
        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];
        if (mItemArray[index] == null) mItemArray[index] = new Item();
        mItemArray[index].position = v;
        if (copyNow) {
            mIOBuffer.reset(index * Item.sizeof);
            mIOBuffer.addF32(v);
            FieldPacker fp = new FieldPacker(8);
            fp.addF32(v);
            mAllocation.setFromFieldPacker(index, 0, fp);
        }
    }

    public void set_size(int index, float v, boolean copyNow) {
        if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */);
        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];
        if (mItemArray[index] == null) mItemArray[index] = new Item();
        mItemArray[index].size = v;
        if (copyNow)  {
            mIOBuffer.reset(index * Item.sizeof + 8);
            mIOBuffer.addF32(v);
            FieldPacker fp = new FieldPacker(4);
            fp.addF32(v);
            mAllocation.setFromFieldPacker(index, 1, fp);
        }
    }

    public Float2 get_position(int index) {
        if (mItemArray == null) return null;
        return mItemArray[index].position;
    }

    public float get_size(int index) {
        if (mItemArray == null) return 0;
        return mItemArray[index].size;
    }

    public void copyAll() {
        for (int ct = 0; ct < mItemArray.length; ct++) copyToArray(mItemArray[ct], ct);
        mAllocation.setFromFieldPacker(0, mIOBuffer);
    }

    public void resize(int newSize) {
        if (mItemArray != null)  {
            int oldSize = mItemArray.length;
            int copySize = Math.min(oldSize, newSize);
            if (newSize == oldSize) return;
            Item ni[] = new Item[newSize];
            System.arraycopy(mItemArray, 0, ni, 0, copySize);
            mItemArray = ni;
        }
        mAllocation.resize(newSize);
        if (mIOBuffer != null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */);
    }
}

Сгенерированный код предоставляется вам для удобства выделения памяти для структур, запрошенных средой выполнения RenderScript, и взаимодействия со struct в памяти. Класс каждой struct определяет следующие методы и конструкторы:

  • Перегруженные конструкторы, позволяющие выделять память. Конструктор ScriptField_ struct_name (RenderScript rs, int count) позволяет вам определить количество структур, для которых вы хотите выделить память, с помощью параметра count . Конструктор ScriptField_ struct_name (RenderScript rs, int count, int usages) определяет дополнительный параметр, usages , который позволяет указать объем памяти для этого выделения памяти. Существует четыре варианта пространства памяти:
    • USAGE_SCRIPT : выделяет пространство памяти сценария. Это пространство памяти по умолчанию, если вы не укажете пространство памяти.
    • USAGE_GRAPHICS_TEXTURE : выделяет пространство текстурной памяти графического процессора.
    • USAGE_GRAPHICS_VERTEX : выделяет пространство вершинной памяти графического процессора.
    • USAGE_GRAPHICS_CONSTANTS : выделяет пространство памяти констант графического процессора, которое используется различными программными объектами.

    Вы можете указать несколько пространств памяти, используя побитовый оператор OR . Это уведомит среду выполнения RenderScript о том, что вы собираетесь получить доступ к данным в указанных областях памяти. В следующем примере память для пользовательского типа данных выделяется как в пространстве памяти скрипта, так и в пространстве вершин:

    Котлин

    val touchPoints: ScriptField_Point = ScriptField_Point(
            myRenderScript,
            2,
            Allocation.USAGE_SCRIPT or Allocation.USAGE_GRAPHICS_VERTEX
    )

    Ява

    ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2,
            Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_VERTEX);
  • Статический вложенный класс Item позволяет создать экземпляр struct в форме объекта. Этот вложенный класс полезен, если имеет смысл работать со struct в вашем коде Android. Когда вы закончите манипулировать объектом, вы можете поместить объект в выделенную память, вызвав set(Item i, int index, boolean copyNow) и установив Item в нужную позицию в массиве. Среда выполнения RenderScript автоматически получает доступ к вновь записанной памяти.
  • Методы доступа для получения и установки значений каждого поля в структуре. Каждый из этих методов доступа имеет index параметр, определяющий struct в массиве, который вы хотите прочитать или записать. Каждый метод установки также имеет параметр copyNow , который указывает, следует ли немедленно синхронизировать эту память со средой выполнения RenderScript. Чтобы синхронизировать любую несинхронизированную память, вызовите copyAll() .
  • Метод createElement() создает описание структуры в памяти. Это описание используется для выделения памяти, состоящей из одного или нескольких элементов.
  • resize() работает так же, как realloc() в C, позволяя вам расширять ранее выделенную память, сохраняя текущие значения, которые были созданы ранее.
  • copyAll() синхронизирует память, установленную на уровне платформы, со средой выполнения RenderScript. Когда вы вызываете метод доступа set для члена, вы можете указать необязательный логический параметр copyNow . Указание true синхронизирует память при вызове метода. Если вы укажете false, вы можете вызвать copyAll() один раз, и он синхронизирует память для всех свойств, которые еще не синхронизированы.

Указатели

Глобальные указатели отражаются в самом классе скрипта, расположенном в project_root/gen/package/name/ScriptC_renderscript_filename . Вы можете объявлять указатели на struct или любой из поддерживаемых типов RenderScript, но struct не может содержать указатели или вложенные массивы. Например, если вы определите следующие указатели на struct и int32_t

typedef struct Point {
    float2 position;
    float size;
} Point_t;

Point_t *touchPoints;
int32_t *intPointer;

затем генерируется следующий код Java:

private ScriptField_Point mExportVar_touchPoints;
public void bind_touchPoints(ScriptField_Point v) {
    mExportVar_touchPoints = v;
    if (v == null) bindAllocation(null, mExportVarIdx_touchPoints);
    else bindAllocation(v.getAllocation(), mExportVarIdx_touchPoints);
}

public ScriptField_Point get_touchPoints() {
    return mExportVar_touchPoints;
}

private Allocation mExportVar_intPointer;
public void bind_intPointer(Allocation v) {
    mExportVar_intPointer = v;
    if (v == null) bindAllocation(null, mExportVarIdx_intPointer);
    else bindAllocation(v, mExportVarIdx_intPointer);
}

public Allocation get_intPointer() {
    return mExportVar_intPointer;
}
  

Создаются метод get и специальный метод с bind_ pointer_name (вместо метода set() ). Метод bind_ pointer_name позволяет вам привязать память, выделенную на виртуальной машине Android, к среде выполнения RenderScript (вы не можете выделить память в файле .rs ). Дополнительную информацию см. в разделе Работа с выделенной памятью .

API распределения памяти

Приложения, использующие RenderScript, по-прежнему работают на виртуальной машине Android. Однако реальный код RenderScript работает изначально и требует доступа к памяти, выделенной на виртуальной машине Android. Для этого необходимо подключить память, выделенную на виртуальной машине, к среде выполнения RenderScript. Этот процесс, называемый привязкой, позволяет среде выполнения RenderScript беспрепятственно работать с памятью, которую она запрашивает, но не может выделить явно. Конечный результат по сути такой же, как если бы вы вызвали malloc в C. Дополнительным преимуществом является то, что виртуальная машина Android может выполнять сбор мусора, а также совместно использовать память со слоем среды выполнения RenderScript. Привязка необходима только для динамически выделяемой памяти. Статически выделенная память автоматически создается для вашего кода RenderScript во время компиляции. См. рисунок 1 для получения дополнительной информации о том, как происходит распределение памяти.

Для поддержки этой системы распределения памяти существует набор API-интерфейсов, которые позволяют виртуальной машине Android выделять память и предлагают функции, аналогичные вызову malloc . Эти классы по существу описывают, как следует выделять память, а также выполняют ее выделение. Чтобы лучше понять, как работают эти классы, полезно представить их в контексте простого вызова malloc , который может выглядеть следующим образом:

array = (int *)malloc(sizeof(int)*10);

Вызов malloc можно разбить на две части: размер выделяемой памяти ( sizeof(int) ) и количество единиц этой памяти, которое должно быть выделено (10). Платформа Android предоставляет классы для этих двух частей, а также класс для представления самого malloc .

Класс Element представляет часть ( sizeof(int) ) вызова malloc и инкапсулирует одну ячейку выделения памяти, например одно значение с плавающей запятой или структуру. Класс Type инкапсулирует Element и количество выделяемых элементов (в нашем примере 10). Вы можете думать о Type как о массиве Element s. Класс Allocation выполняет фактическое распределение памяти на основе заданного Type и представляет фактическую выделенную память.

В большинстве ситуаций вам не нужно напрямую вызывать эти API выделения памяти. Классы отраженного слоя автоматически генерируют код для использования этих API, и все, что вам нужно сделать для выделения памяти, — это вызвать конструктор, который объявлен в одном из классов отраженного слоя, а затем связать полученное Allocation памяти с RenderScript. В некоторых ситуациях вы захотите использовать эти классы напрямую для самостоятельного выделения памяти, например, при загрузке растрового изображения из ресурса или когда вы хотите выделить память для указателей на примитивные типы. Вы можете увидеть, как это сделать, в разделе «Выделение и привязка памяти к RenderScript» . В следующей таблице более подробно описаны три класса управления памятью:

Тип объекта Android Описание
Element

Элемент описывает одну ячейку выделения памяти и может иметь две формы: базовую или сложную.

Базовый элемент содержит один компонент данных любого допустимого типа данных RenderScript. Примеры типов данных базового элемента включают одно значение float , вектор float4 или один цвет RGB-565.

Сложные элементы содержат список базовых элементов и создаются из struct , которые вы объявляете в своем коде RenderScript. Например, выделение может содержать несколько struct , расположенных в памяти по порядку. Каждая структура рассматривается как отдельный элемент, а не каждый тип данных внутри этой структуры.

Type

Тип представляет собой шаблон выделения памяти и состоит из элемента и одного или нескольких измерений. Он описывает структуру памяти (по сути, массив Element ), но не выделяет память для данных, которые он описывает.

Тип состоит из пяти измерений: X, Y, Z, LOD (уровень детализации) и Faces (карты-куба). Вы можете установить для размеров X,Y,Z любое положительное целочисленное значение в пределах ограничений доступной памяти. Распределение одного измерения имеет измерение X больше нуля, а измерения Y и Z равны нулю, что указывает на отсутствие. Например, распределение x=10, y=1 считается двумерным, а x=10, y=0 считается одномерным. Измерения LOD и Faces являются логическими значениями, обозначающими наличие или отсутствие.

Allocation

Выделение предоставляет память для приложений на основе описания памяти, представленного Type . Выделенная память может существовать во многих областях памяти одновременно. Если память изменяется в одном пространстве, вы должны явно синхронизировать память, чтобы она обновлялась во всех других пространствах, в которых она существует.

Данные о распределении загружаются одним из двух основных способов: с установленным типом и с непроверенным типом. Для простых массивов существуют функции copyFrom() , которые берут массив из системы Android и копируют его в хранилище памяти собственного уровня. Непроверенные варианты позволяют системе Android копировать массивы структур, поскольку она не поддерживает структуры. Например, если имеется выделение, представляющее собой массив из n чисел с плавающей запятой, можно скопировать данные, содержащиеся в массиве float[n] или массиве byte[n*4] .

Работа с памятью

Нестатические глобальные переменные, которые вы объявляете в своем RenderScript, выделяются в памяти во время компиляции. Вы можете работать с этими переменными непосредственно в коде RenderScript, не выделяя для них память на уровне платформы Android. Уровень платформы Android также имеет доступ к этим переменным с помощью предоставленных методов доступа, которые генерируются в отраженных классах слоя. Если эти переменные инициализируются на уровне среды выполнения RenderScript, эти значения используются для инициализации соответствующих значений на уровне платформы Android. Если глобальные переменные помечены как const, метод set не создается. Посмотрите здесь для более подробной информации.

Примечание. Если вы используете определенные структуры RenderScript, которые содержат указатели, такие как rs_program_fragment и rs_allocation , вам необходимо сначала получить объект соответствующего класса платформы Android, а затем вызвать метод set для этой структуры, чтобы привязать память к среде выполнения RenderScript. Вы не можете напрямую манипулировать этими структурами на уровне среды выполнения RenderScript. Это ограничение не применимо к пользовательским структурам, содержащим указатели, поскольку их вообще невозможно экспортировать в отраженный класс слоя. Ошибка компилятора генерируется, если вы пытаетесь объявить нестатическую глобальную структуру, содержащую указатель.

RenderScript также поддерживает указатели, но вы должны явно выделить память в коде платформы Android. Когда вы объявляете глобальный указатель в файле .rs , вы выделяете память через соответствующий класс отраженного слоя и привязываете эту память к собственному слою RenderScript. Вы можете взаимодействовать с этой памятью на уровне платформы Android, а также на уровне RenderScript, который дает вам возможность гибко изменять переменные на наиболее подходящем уровне.

Выделение и привязка динамической памяти к RenderScript

Чтобы выделить динамическую память, вам необходимо вызвать конструктор класса Script.FieldBase , что является наиболее распространенным способом. Альтернативой является создание Allocation вручную, которое требуется для таких вещей, как указатели примитивных типов. Для простоты вам следует использовать конструктор класса Script.FieldBase , когда он доступен. После получения выделения памяти вызовите отраженный метод bind указателя, чтобы привязать выделенную память к среде выполнения RenderScript.

В приведенном ниже примере память выделяется как для указателя примитивного типа intPointer , так и для указателя на структуру touchPoints . Он также привязывает память к RenderScript:

Котлин

private lateinit var myRenderScript: RenderScript
private lateinit var script: ScriptC_example
private lateinit var resources: Resources

public fun init(rs: RenderScript, res: Resources) {
    myRenderScript = rs
    resources = res

    // allocate memory for the struct pointer, calling the constructor
    val touchPoints = ScriptField_Point(myRenderScript, 2)

    // Create an element manually and allocate memory for the int pointer
    val intPointer: Allocation = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2)

    // create an instance of the RenderScript, pointing it to the bytecode resource
    script = ScriptC_point(myRenderScript/*, resources, R.raw.example*/)

    // bind the struct and int pointers to the RenderScript
    script.bind_touchPoints(touchPoints)
    script.bind_intPointer(intPointer)

   ...
}

Ява

private RenderScript myRenderScript;
private ScriptC_example script;
private Resources resources;

public void init(RenderScript rs, Resources res) {
    myRenderScript = rs;
    resources = res;

    // allocate memory for the struct pointer, calling the constructor
    ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2);

    // Create an element manually and allocate memory for the int pointer
    intPointer = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2);

    // create an instance of the RenderScript, pointing it to the bytecode resource
    script = new ScriptC_example(myRenderScript, resources, R.raw.example);

    // bind the struct and int pointers to the RenderScript
    script.bind_touchPoints(touchPoints);
    script.bind_intPointer(intPointer);

   ...
}

Чтение и запись в память

Вы можете читать и записывать в статически и динамически выделенную память как на уровне среды выполнения RenderScript, так и на уровне платформы Android.

Статически выделенная память имеет ограничение односторонней связи на уровне выполнения RenderScript. Когда код RenderScript изменяет значение переменной, оно не передается обратно на уровень платформы Android в целях повышения эффективности. Последнее значение, установленное в платформе Android, всегда возвращается во время вызова метода get . Однако когда код платформы Android изменяет переменную, это изменение может быть автоматически передано в среду выполнения RenderScript или синхронизировано позднее. Если вам нужно отправить данные из среды выполнения RenderScript на уровень платформы Android, вы можете использовать функцию rsSendToClient() чтобы обойти это ограничение.

При работе с динамически выделяемой памятью любые изменения на уровне среды выполнения RenderScript передаются обратно на уровень платформы Android, если вы изменили распределение памяти с помощью связанного с ним указателя. Изменение объекта на уровне платформы Android немедленно распространяет изменения обратно на уровень среды выполнения RenderScript.

Чтение и запись в глобальные переменные

Чтение и запись в глобальные переменные — простой процесс. Вы можете использовать методы доступа на уровне платформы Android или установить их непосредственно в коде RenderScript. Имейте в виду, что любые изменения, которые вы вносите в код RenderScript, не передаются обратно на уровень платформы Android (подробнее см. здесь ).

Например, учитывая следующую структуру, объявленную в файле с именем rsfile.rs :

typedef struct Point {
    int x;
    int y;
} Point_t;

Point_t point;

Вы можете присвоить значения структуре следующим образом непосредственно в rsfile.rs . Эти значения не передаются обратно на уровень платформы Android:

point.x = 1;
point.y = 1;

Вы можете присвоить значения структуре на уровне платформы Android следующим образом. Эти значения асинхронно передаются обратно на уровень выполнения RenderScript:

Котлин

val script: ScriptC_rsfile = ...

...

script._point = ScriptField_Point.Item().apply {
    x = 1
    y = 1
}

Ява

ScriptC_rsfile script;

...

Item i = new ScriptField_Point.Item();
i.x = 1;
i.y = 1;
script.set_point(i);

Вы можете прочитать значения в коде RenderScript следующим образом:

rsDebug("Printing out a Point", point.x, point.y);

Вы можете прочитать значения на уровне платформы Android с помощью следующего кода. Имейте в виду, что этот код возвращает значение, только если оно было установлено на уровне платформы Android. Вы получите исключение нулевого указателя, если установите значение только на уровне времени выполнения RenderScript:

Котлин

Log.i("TAGNAME", "Printing out a Point: ${mScript._point.x} ${mScript._point.y}")
println("${point.x} ${point.y}")

Ява

Log.i("TAGNAME", "Printing out a Point: " + script.get_point().x + " " + script.get_point().y);
System.out.println(point.get_x() + " " + point.get_y());

Чтение и запись глобальных указателей

Предполагая, что память выделена на уровне платформы Android и привязана к среде выполнения RenderScript, вы можете читать и записывать память на уровне платформы Android, используя методы get и set для этого указателя. На уровне среды выполнения RenderScript вы можете читать и записывать в память с помощью указателей, как обычно, и изменения распространяются обратно на уровень платформы Android, в отличие от статически выделенной памяти.

Например, имеется следующий указатель на struct в файле с именем rsfile.rs :

typedef struct Point {
    int x;
    int y;
} Point_t;

Point_t *point;

Предполагая, что вы уже выделили память на уровне платформы Android, вы можете получить доступ к значениям в struct как обычно. Любые изменения, которые вы вносите в структуру через ее переменную-указатель, автоматически становятся доступными на уровне платформы Android:

Котлин

point[index].apply {
    x = 1
    y = 1
}

Ява

point[index].x = 1;
point[index].y = 1;

Вы также можете читать и записывать значения в указатель на уровне платформы Android:

Котлин

val i = ScriptField_Point.Item().apply {
    x = 100
    y = 100
}
val p = ScriptField_Point(rs, 1).apply {
    set(i, 0, true)
}
script.bind_point(p)

p.get_x(0)            //read x and y from index 0
p.get_y(0)

Ява

ScriptField_Point p = new ScriptField_Point(rs, 1);
Item i = new ScriptField_Point.Item();
i.x=100;
i.y = 100;
p.set(i, 0, true);
script.bind_point(p);

p.get_x(0);            //read x and y from index 0
p.get_y(0);

Если память уже привязана, вам не нужно повторно привязывать ее к среде выполнения RenderScript каждый раз, когда вы вносите изменение в значение.