Skip to content

Commit

Permalink
Add a "stress test" android activity that draws lots of Martys
Browse files Browse the repository at this point in the history
This also required adding a "transform" method to the low level rendering API.

Diffs=
74e53aa12 Add a "stress test" android activity that draws lots of Martys (#5398)

Co-authored-by: Chris Dalton <[email protected]>
  • Loading branch information
csmartdalton and csmartdalton committed Jul 31, 2023
1 parent 0eddc41 commit 4c2ab0c
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6e07d855c7adaba20b0fd985987f2ec32a5d2dab
74e53aa129f5a03160ed395b23e5074d592fc307
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@
android:theme="@style/AppTheme" />
<activity android:name=".FrameActivity" />
<activity android:name=".DynamicTextActivity" />
<activity android:name=".StressTestActivity" />

<!-- For testing -->
<activity android:name=".SingleActivity" />
</application>

</manifest>
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/app/rive/runtime/example/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class MainActivity : AppCompatActivity() {
Pair(R.id.go_compose, ComposeActivity::class.java),
Pair(R.id.go_frame, FrameActivity::class.java),
Pair(R.id.go_dynamic_text, DynamicTextActivity::class.java),
)
Pair(R.id.go_stress_test, StressTestActivity::class.java),
)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down
152 changes: 152 additions & 0 deletions app/src/main/java/app/rive/runtime/example/StressTestActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package app.rive.runtime.example

import android.content.Context
import android.graphics.RectF
import android.os.Bundle
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import app.rive.runtime.kotlin.RiveTextureView
import app.rive.runtime.kotlin.core.*
import app.rive.runtime.kotlin.renderers.Renderer
import java.util.*
import android.view.MotionEvent
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleObserver
import kotlin.math.*


class StressTestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_stress_test)

// Hides the app/action bar
supportActionBar?.hide()

// Attach the Rive view to the activity's root layout
val layout = findViewById<ViewGroup>(R.id.low_level_view_root)
val riveView = StressTestView(this)

layout.addView(riveView)
}
}

class StressTestView(context: Context) : RiveTextureView(context) {
private var instanceCount : Int = 1
private var totalElapsed : Float = 0f
private var totalFrames : Int = 0

override fun createObserver(): LifecycleObserver {
return object : DefaultLifecycleObserver {
/* Optionally override lifecycle methods. */
// override fun onCreate(owner: LifecycleOwner) {
// super.onCreate(owner)
// }
// override fun onDestroy(owner: LifecycleOwner) {
// super.onDestroy(owner)
// }
}
}

override fun createRenderer(): Renderer {
val renderer = object : Renderer() {

override fun draw() {
val step = animationInstance.effectiveDurationInSeconds / instanceCount
artboard.let {
save()
align(Fit.CONTAIN, Alignment.CENTER, RectF(0f, 0f, width, height), it.bounds)
val rows = (instanceCount + 6) / 7
val cols = min(instanceCount, 7)
translate(0f, (rows - 1) * -.5f * 200f)
for (j in 1 .. rows) {
save()
translate((cols - 1) * -.5f * 125f, 0f)
for (i in 1..cols) {
it.drawSkia(cppPointer)
// Draw each Marty at a slightly different animation offset to make sure
// the renderer can't cache things. This loop will advance the animation
// until it loops back around to the original point we started at before
// drawing.
animationInstance.advance(step)
animationInstance.apply()
artboard.advance(step)
translate(125f, 0f)
}
restore()
translate(0f, 200f)
}
restore()
}
}

override fun advance(elapsed: Float) {
// Actually advance the animation here. draw() will also call advance(), but the
// purpose of that is to draw each Marty at a slightly different animation offset,
// and it will loop back around to the original animation location.
animationInstance.advance(elapsed)
animationInstance.apply()
artboard.advance(elapsed)

totalElapsed += elapsed
totalFrames++

if (totalElapsed > 1f) {
val fps = totalFrames / totalElapsed
val fpsView =
((getParent() as ViewGroup).getParent() as ViewGroup).getChildAt(1) as TextView
fpsView.text =
java.lang.String.format(
Locale.US,
"%d instances @ %.1f FPS (%.2f ms)",
instanceCount,
fps,
1e3f / fps
)
totalElapsed = 0f
totalFrames = 0
}
}
}
// Call setup file only once we created the renderer.
setupFile(renderer)
return renderer
}

private lateinit var file: File

// Objects that the renderer needs for drawing
private lateinit var artboard: Artboard
private lateinit var animationInstance: LinearAnimationInstance

private fun setupFile(renderer: Renderer) {
// Keep a reference to the file to keep resources around.
file = resources.openRawResource(R.raw.marty).use { File(it.readBytes()) }
artboard = file.firstArtboard
animationInstance = artboard.animation(1)

// This will be deleted with its dependents.
renderer.dependencies.add(file)
}

override fun onTouchEvent(event: MotionEvent): Boolean {

val action: Int = event.getActionMasked()

return when (action) {
MotionEvent.ACTION_DOWN -> {
if (instanceCount < 7)
instanceCount += 2
else
instanceCount += 7
totalElapsed = 0f
totalFrames = 0
val fpsView = ((getParent() as ViewGroup).getParent() as ViewGroup).getChildAt(1) as TextView
fpsView.text = java.lang.String.format("%d instances", instanceCount)
true
}
else -> super.onTouchEvent(event)
}
}
}
34 changes: 34 additions & 0 deletions app/src/main/res/layout/activity_stress_test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StressTestActivity">

<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp">

<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/low_level_view_root"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="99" />

<TextView
android:id="@+id/fps"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_weight="1"
android:layout_marginTop="6dp"
android:text="1 instances"
android:textAppearance="@style/InfoTextMedium" />
</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
9 changes: 9 additions & 0 deletions app/src/main/res/layout/main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@
android:text="Dynamic Text"
android:textColor="@color/textColorPrimary" />

<Button
android:id="@+id/go_stress_test"
style="@style/Widget.Material3.Button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:text="Stress Test"
android:textColor="@color/textColorPrimary" />

</LinearLayout>

</ScrollView>
Expand Down
Binary file added app/src/main/res/raw/marty.riv
Binary file not shown.
Binary file added app/src/main/res/raw/paper.riv
Binary file not shown.
Binary file added app/src/main/res/raw/runner.riv
Binary file not shown.
15 changes: 15 additions & 0 deletions kotlin/src/main/cpp/src/bindings/bindings_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ extern "C"
jniWrapper->getRendererOnWorkerThread()->align(fit, alignment, targetBounds, sourceBounds);
}

JNIEXPORT void JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_cppTransform(JNIEnv* env,
jobject thisObj,
jlong ref,
jfloat x,
jfloat sy,
jfloat sx,
jfloat y,
jfloat tx,
jfloat ty)
{
JNIRenderer* jniWrapper = reinterpret_cast<JNIRenderer*>(ref);
jniWrapper->getRendererOnWorkerThread()->transform(rive::Mat2D(x, sy, sx, y, tx, ty));
}

JNIEXPORT jint JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_cppWidth(JNIEnv*, jobject, jlong rendererRef)
{
Expand Down
23 changes: 23 additions & 0 deletions kotlin/src/main/java/app/rive/runtime/kotlin/renderers/Renderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ abstract class Renderer(
targetBounds: RectF,
srcBounds: RectF
)
private external fun cppTransform(
cppPointer: Long,
x: Float,
sy: Float,
sx: Float,
y: Float,
tx: Float,
ty: Float
)

/** Instantiates JNIRenderer in C++ */
private external fun constructor(trace: Boolean, type: Int): Long
Expand Down Expand Up @@ -168,6 +177,20 @@ abstract class Renderer(
)
}

fun transform(x: Float, sy: Float, sx: Float, y: Float, tx: Float, ty: Float) {
cppTransform(cppPointer, x, sy, sx, y, tx, ty)
}

fun scale(sx: Float, sy: Float)
{
transform(sx, 0f, 0f, sy, 0f, 0f)
}

fun translate(dx: Float, dy: Float)
{
transform(1f, 0f, 0f, 1f, dx, dy)
}

@CallSuper
override fun doFrame(frameTimeNanos: Long) {
if (isPlaying) {
Expand Down

0 comments on commit 4c2ab0c

Please sign in to comment.