Qualcomm Enhanced SDK (QESDK) API Reference (10)

Qualcomm Enhanced SDK (QESDK) API Reference (10)

    • 4.4 Encoding and decoding protobuf messages
      • 4.4.1 Request message
      • 4.4.2 Decoding messages
    • 4.5 Sample code
  • 5 Performance API (QAPE)
    • 5.1 Performance API Overview
    • 5.2 QAPE API
      • 5.2.1 QAPE Manager
      • 5.2.2 set_pkg
      • 5.2.3 boost_cpu
      • 5.2.4 boost_gpu
      • 5.2.5 hint_low_latency
      • 5.2.6 hint_high_cpuutil
      • 5.2.7 hint_low_cpuutil
      • 5.2.8 hint_thread_pipeline
      • 5.2.9 release_thread_hints

4.4 Encoding and decoding protobuf messages

4.4.1 Request message

The sensor subsystem can only understand protocol buffer-encoded requests. The sns_client_request_msg must be prepared to enable the activity_recognition sensor through the protocol buffer Java API (generated from the .proto file).

Encoding request message

To send all types of client requests to the sensor subsystem, the sns_client_request_msg proto message should be used and must be encoded using the generated Java Protobuf API.

To fill in the values in the required fields of sns_client_request_msg, obtain the builder for this message via the newBuilder() method.

SUID request

The sensor subsystem contains SUID sensors, which have mappings for all sensors available on the subsystem. Upon receiving a SUID request, the SUID sensor provides the SUID of the sensor that the client is looking for.

To send a SUID request, sns_client_request_msg must be encoded with the SUID request configuration. The sns_std_suid and suspend_config sub-messages must be populated as per the SUID request, the sns_suid_req message is used for the SUID request, the upper and lower SUID limits must be “12370169555311111083”, the values must be populated as follows:

 sns_suid_req.Builder suid_req_build = sns_suid_req.newBuilder();
                    suid_req_build.setDataType("activity_recognition");
                    suid_req_build.setDefaultOnly(true);
                    suid_req_build.setRegisterUpdates(true);
                    sns_std_suid.Builder std_suid_build = sns_std_suid.newBuilder();
                    LOOKUP_SUID = Long.parseUnsignedLong("12370169555311111083")
                    std_suid_build.setSuidHigh(LOOKUP_SUID);
                    std_suid_build.setSuidLow(LOOKUP_SUID);
                    sns_std_suid std_suid = std_suid_build.build();

The padded sns_suid_req message must be set to the payload field of sns_std_request as follows:

 sns_std_request.Builder std_request = sns_std_request.newBuilder();
                    std_request.setPayload(suid_req_build.build().toByteString());
                    
                    sns_std_request must be set as request field for sns_client_request_msg as follows:
                    request_msg.setRequest(std_request.build());
                    sns_client_request_msg.Builder client_req_msg = sns_client_request_msg.newBuilder();
                    client_req_msg.setMsgId(sns_suid_msgid.SNS_SUID_MSGID_SNS_SUID_REQ_VALUE);
                    client_req_msg.setSuid(std_suid);
                    client_req_msg.setSuspConfig(suspend_config.build());
                    client_req_msg.setRequest(std_request);

client_req_msg should be converted to a byteArray and sent to the sensor subsystem using the send_request() API.

 byte[] bytedata = request_msg.build().toByteArray();

Sns Builder

sns_client_request_msg.Builder request_msg=sns_client_request_msg.newBuilder();

sns_client_request_msg contains sub-messages, namely sns_std_suid, suspend_config and sns_std_request. sns_std_suid is used to hold the SUID of the request-specific sensor.

The following code encodes the sns_std_suid sub-message:

sns_std_suid.Builder std_suid_build = sns_std_suid.newBuilder();
            std_suid_build.setSuidHigh(SuidHigh);
            std_suid_build.setSuidLow(SuidLow);
            sns_std_suid std_suid = std_suid_build.build();

suspend_config is used to save the transport type configuration of the client processor.

The following example code is used to encode this sub-message:

sns_client_request_msg.suspend_config.Builder suspend_config = sns_client_request_msg.suspend_config.newBuilder();
            suspend_config.setClientProcType(sns_std_client_processor.SNS_STD_CLIENT_PROCESSOR_APSS);
            suspend_config.setDeliveryType(sns_client_delivery.SNS_CLIENT_DELIVERY_WAKEUP);

sns_std_request is used to set batch configuration, send property requests and SUID requests.

Start sensor via configuration

If a client wants to collect data from a specific sensor on a sensor subsystem, it must send a special protobuf-encoded sns_client_request_msg initiation request to the subsystem using the send_request API.

The sns_std_suid submessage must be populated with the SUID value of the sensor to be started. The suspend_config submessage must be populated based on the client type.

The value of the sns_std_request sub-message must be populated as follows:

sns_std_request.Builder std_request = sns_std_request.newBuilder();
            request_msg.setSuid(std_suid);
            request_msg.setMsgId(SNS_STD_SENSOR_MSGID_SNS_STD_ON_CHANGE_CONFIG_VALUE); // MsgId 514 is for on-change Sensor configuration
            request_msg.setSuspConfig(suspend_config.build());

The populated sns_std_request must be set to the request field of sns_client_request_msg as follows:

request_msg.setRequest(std_request.build());

client_req_msg should be converted to a byteArray and sent to the sensor subsystem using the send_request() API.

byte[] bytedata = request_msg.build().toByteArray();

4.4.2 Decoding messages

void onValues(byte[] bytes, int size, int sensor_session_id)

This method needs to be registered as an event callback in advance and then triggered by the QESDK sensor manager when a protobuf encoded byte array event occurs.

This byte array event data can be converted into a Protobuf client event message using the Protobuf-generated API parseFrom().

sns_client_event_msg decode_request_msg=sns_client_event_msg.parseFrom(proto_message);

//proto_message is byte array
The event may be an Activity_Recognition or SUID event, identified by the event ID.

SUID event message

sns_suid_msgid.SNS_SUID_MSGID_SNS_SUID_EVENT_VALUE

For SUID events, the SUID data is nested within the main event message. To get the actual SUID data, the nested SUID sub-message must be parsed.

SnsStdType.sns_std_suid suid_event = null;
suid_event=SnsSuid.sns_suid_event.parseFrom(decode_request_msg.getEvents(i).getPayload()).getSuid(0)

AR event messages

sns_activity_recognition_msgid.SNS_ACTIVITY_RECOGNITION_MSGID_SNS_AR_EVENT_VALUE

For Activity_Recognition, the AR event data is nested within the main event message. To get the actual AR data, the nested AR sub-message must be parsed.

sns_ar_event ar_event = null;
ar_event=SnsActivityRecognition.sns_ar_event.parseFrom(decode_request_msg.getEvents(i).getPayload());

4.5 Sample Code

Get QESDK SensorsManager instance

qesdkManager = IQesdk.createInstance(getApplicationContext());
                if (qesdkManager != null) {<!-- -->
                sessionId = doInit();
                sensorsManager = new SensorsManager(qesdkManager);
                Log.e("QesdAPP", "Returned Session id: " + sessionId);
                if (sensorsManager == null) {<!-- -->
                Log.e("QesdAPP", "sensorsManager is NULL");
                }
                }

Create session

if (sensorsManager != null) {<!-- -->
                int ret = 0;
                try {<!-- -->
                sensor_session_id = sensorsManager.create_session();
                } catch (QesdkStatusException e) {<!-- -->
                Log.e("QesdAPP", "Session id: " + sessionId + "Error:" + e);
                }
                }

Register event callback

try {<!-- -->
                sensorsManager.register_event_callback(sensor_session_id, new ISensorsCBs.Ievent_callback_type() {<!-- -->
                @Override
                public void onValues(byte[] bytes, int size, int sensor_session_id) {<!-- -->
                byte[] proto_event_message = new byte[size];
                proto_event_message = Arrays.copyOf(bytes, size);
                String events = ProtobufTest.decodeEvent(proto_event_message);
                updateData(events); }
                });
                }catch (QesdkStatusException e) {<!-- -->
                Log.e("QesdAPP", "Session id: " + sessionId + "Error:" + e);
                }

Get available sensors

try {<!-- -->
                sensors_list = sensorsManager.get_sensors_list();
                } catch (QesdkStatusException e) {<!-- -->
                Log.e("QesdAPP", "fail to get API result Session id: " + sessionId + "Error:" + e);
                }

send request

byte[] proto_message = null;
                try {<!-- -->
                proto_message = getRequest(); //This function returns protobuf encoded
                //request message, refer section 4.2.1.1
                } catch (InvalidProtocolBufferException e) {<!-- -->
                e.printStackTrace();
                Log.e("QesdAPP", "protobuf exeption happens");
                return;
                } catch (IOException e) {<!-- -->
                e.printStackTrace();
                Log.e("QesdAPP", "IO exeption happens");
                return;
                }
                int proto_message_size = proto_message.length;
                Log.d("QesdAPP", "trying to call send_request sessionId = " + sessionId + "sensor_session_id = " + sensor_session_id + "protobuf size = " + proto_message_size);
                try {<!-- -->
                ret = sensorsManager.send_request(sensor_session_id, proto_message, proto_message_size);
                } catch (QesdkStatusException e) {<!-- -->
                Log.e("QesdAPP
                , "fail to get API result Session id: " + sessionId + "sensor_session_id = " + sensor_session_id + "Error:" + e);
                }

5 Performance API (QAPE)

Application developers can use Qualcomm Adaptive Performance Engine (QAPE) to annotate an application’s dynamic performance requirements to the underlying system software. The underlying QAPE framework can map hints to the correct CPU cores. This helps application developers become more productive as they no longer need to keep track of the CPU configuration for each SoC.

These APIs are very powerful and enable application workload hints. This helps achieve a good balance between performance and power consumption of the SoC. With these APIs, application developers can ensure optimal performance on a Qualcomm device without having to be familiar with information such as the CPU topology, CPU frequency, and GPU frequency of a particular SoC.

Keep the following points in mind when using these APIs:

  • Any thread can use a combination of these APIs, for example, you can use hint_thread_pipeline() to hint that a thread is a pipeline thread, and use hint_high_util() to mark the thread as high utilization. However, marking all pipeline threads as highly utilized may result in increased SoC power consumption/temperature rise and should be used with caution. See Appendix A for details.
  • These APIs are persistent. When any thread is marked as a low-latency, high-utilization, or pipeline thread, the corresponding prompt remains throughout the thread’s lifetime.

Threads marked only for low latency are expected to run for shorter periods of time (low CPU utilization) but have stricter latency requirements.

5.1 Performance API Overview

This section provides an interface to assist applications in labeling workloads, which the underlying system can use to make optimal decisions about frequency scaling (CPU/GPU), CPU task placement, and more.

QAPE provides application developers with a rich set of capabilities to assist in workload annotation to get the most out of the SoC/hardware. This provides application developers with a dedicated set of custom APIs to prompt the system for their performance requirements without having to worry about underlying hardware characteristics/configuration.

Advantages

Category QAPE
Speed-up mode CPU load, GPU load, task scheduling and layout tips
Frequency boost Indirect (scheduler/DCVS determines frequency)
Hardware abstraction Developers do not need to determine SoC information and frequency
Automatic lock release/acquisition Automatically release (onPause) and acquire (onResume) according to the activity life cycle

5.2 QAPE API

5.2.1 QAPE Manager

The main link enters the constructor of the QAPE subsystem.

QAPEManager()

Parameters
qesdk
Proven IQesdk interface
Return results
QAPEManager
subsystem manager object

5.2.2 set_pkg

Binds the package name to the current session, if this API is not called, a session with the performance API will not be established and any calls to the performance API and errors will return -2.

set_pkg()

Note:
This API is required to maintain internal session details.

5.2.3 boost_cpu

Increases the virtual utilization of all available CPUs by a percentage specified by the input parameter. This load is used to calculate the CPU frequency, the greater the load, the higher the CPU frequency. The duration of this API call is 2 s. This helps provide applications with a momentary boost (higher CPU frequency) when a sudden increase in CPU load is expected. For example, scene switches in CPU-heavy games.

boost_cpu()

Usage Instructions

This API is required when the application anticipates an imminent increase in load that will cause the application to miss deadlines (e.g. vsync). Since the duration of this API call is 2 seconds, if the application needs to extend the speed-up duration, it will need to call this callout again. The ideal situation is for the underlying CPU DCVS software to lock in the required load requirements immediately after a load burst. This eliminates the need to call this API repeatedly and simply prepares the underlying CPU DCVS for upcoming load bursts. For customized low-power use cases, negative values can be used to reduce utilization.

Note:
This API can only be called 3 times in 10 seconds.

Note:
The boost_cpu API is for OEM use only. Third-party use is not supported.


boost_val example

Notice:
Continuous speed increases may consume more power and may affect overall battery life. Recommended when a sudden increase in CPU workload is expected.

If the utilization calculated based on the actual load of the CPU is 30, depending on the boost_cpu percentage value, then the utilization of each CPU will be updated appropriately. CPU utilization ranges from 0 – 1024 (1024 represents maximum primary CPU utilization). Depending on the cluster of CPUs, the maximum CPU utilization may be lower than 1024 (for example, small core/big core cluster). The CPU governor adjusts the frequency of the cluster at defined intervals based on the utilization reported by all CPUs in the cluster.

5.2.4 boost_gpu

Increases the GPU load on the GPU by a percentage specified by the input parameter boost_val. The duration of the API call is 2 s. This API is required when the application anticipates a transient increase in GPU load. For example, scene switches in GPU-heavy games.

boost_gpu()

Notice:
Continuous speed increases may consume more power and may affect overall battery life. Recommended when a sudden increase in GPU workload is expected.

Note:
This API can only be called 3 times in 10 seconds.

Note:
The boost_gpu API is for OEM use only. Third-party use is not supported.

5.2.5 hint_low_latency

Notifies the system that a short-running critical application thread must be scheduled immediately on the CPU. The system marks threads specified by the application as low-latency threads. This annotation does not affect the distribution of threads on large (high performance) cores and small (low performance) cores. Additionally, if any thread is prompted as a low-latency thread, the kernel scheduler attempts to allocate the CPU immediately, thus avoiding scheduling delays. This hint is beneficial for any task-critical thread that needs to complete early on the CPU (low latency requirements), such as an asynchronous I/O thread that needs immediate attention once its wait time has expired. The ideal situation is that thread CPU workload is generally low; but it needs to be run as early as possible when it is ready to run.

hint_low_latency()

Notice:
Suppose there are many application threads identified as low latency. In this case, the overall hint advantage is reduced because the system scheduler does not further differentiate between these groups of low-latency threads. Any thread hints (such as high_cpuutil, low_cpuutil, and thread_pipeline) are retained throughout the lifetime of the thread unless the application explicitly releases them using the release_thread_hints API or through the automatic release mechanism when the application enters the background or terminates. release.

5.2.6 hint_high_cpuutil

Notifies the system that there is a thread with high CPU utilization that requires the use of high-performance cores on the SoC. The thread is assigned to the CPU large core accordingly. This annotation does not set the core frequency used by the thread when running, but leaves the choice to the DCVS scheduler directly or indirectly through the boost_cpu API.

hint_high_cpuutil()

Notice:
When defining a thread as highly utilized, the system attempts to use large/performance cores first. This may result in increased power consumption and may impact battery life. Any thread hints (such as high_cpuutil, low_cpuutil, and thread_pipeline) are retained throughout the lifetime of the thread unless the application explicitly releases them using the release_thread_hints API or through the automatic release mechanism when the application enters the background or terminates. release.

Usage Instructions

This tip is useful for critical threads running huge workloads, such as the UI thread in an application or the GL thread of a game. This tip is also useful when application thread workload is typically high but occasionally drops. Using this annotation helps the system scheduler ensure that application threads remain on large/performance CPU cores. One of the key goals of this annotation is to enable application threads to avoid using thread affinity techniques that require knowledge of specific CPU topologies. Additionally, during runtime, this hint may be detrimental to application threads if the associated CPU is already running other concurrent workloads.

5.2.7 hint_low_cpuutil

Notifies the system that there are threads with low CPU utilization that do not require the use of high-performance cores. For example, threads that perform background work do not require high-performance cores even if the load occasionally increases. This hint does not set the core frequency used by the thread scheduling runtime, but leaves the choice to the DCVS scheduler, either directly or indirectly via the boost_cpu API.

hint_low_cpuutil()

Notice:
This API needs to be used with caution, as any threads marked as low-utilized will not run on large cores. Any thread hints (such as high_cpuutil, low_cpuutil, and thread_pipeline) are retained throughout the lifetime of the thread unless the application explicitly releases them using the release_thread_hints API or through the automatic release mechanism when the application enters the background or terminates. release.

Note:
hint_low_cpuutil / hint_high_cpuutil are not the same as thread affinity. Using thread CPU affinity, developers need to determine the cluster configuration and cores on which the thread can run. With hint_high_cpuutil, threads can run on any core on Big Core/Advanced Big Core, whereas with CPU affinity, high utilization threads may wait in the queue while the core is busy with other threads, and affinity may be counterproductive. Hint_low_cpuutil has similar behavior.

5.2.8 hint_thread_pipeline

Provides a list of threads required to run each Vsync/draw cycle. This API flags the underlying system to use a higher priority CPU scheduling priority to handle these pipeline threads. It will tell the system that these pipeline threads are important and require certain guarantees from the SoC system during each Vsync cycle of the application’s rendering cycle.

hint_thread_pipeline()

Example use cases

For games using Unreal Engine, these pipeline threads can be the UI/game thread, rendering thread, and RHI thread. Some of these pipeline threads can also be flagged for high CPU utilization prompts. Any thread hints (such as high_cpuutil, low_cpuutil, and thread_pipeline) are retained throughout the lifetime of the thread unless the application explicitly releases them using the release_thread_hints API or through the automatic release mechanism when the application enters the background or terminates. release.

5.2.9 release_thread_hints

Used to release the thread marked hint, any thread will be reset to the default state. release_hint works on any thread.

release_thread_hints()