Skip to content

Commit

Permalink
Adoption of RenderNode approach (#1014)
Browse files Browse the repository at this point in the history
It is a simplified adoption of Android's `RenderNode`.

```kt
/**
 * <p>RenderNode is used to build hardware accelerated rendering hierarchies. Each RenderNode
 * contains both a display list as well as a set of properties that affect the rendering of the
 * display list. RenderNodes are used internally for all Views by default and are not typically
 * used directly.</p>
 *
 * <p>RenderNodes are used to divide up the rendering content of a complex scene into smaller
 * pieces that can then be updated individually more cheaply. Updating part of the scene only needs
 * to update the display list or properties of a small number of RenderNode instead of redrawing
 * everything from scratch. A RenderNode only needs its display list re-recorded when its content
 * alone should be changed. RenderNodes can also be transformed without re-recording the display
 * list through the transform properties.</p>
 ```
 
 This is a more correct approach to make `GraphicsLayer` in Compose invalidation independently.
 - Moved drawing callback to C++ side to avoid extra interop costs
 - Switched from `SkPicture` placeholder to custom `SkDrawable` implementation
  • Loading branch information
MatkovIvan authored Jan 30, 2025
1 parent 1af72c1 commit 09dae79
Show file tree
Hide file tree
Showing 15 changed files with 1,660 additions and 1 deletion.
3 changes: 3 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This project contains code adapted from "The Android Open Source Project" licensed under the Apache License, Version 2.0 (the "License"):

https://android.googlesource.com/platform/frameworks/base
2 changes: 1 addition & 1 deletion skiko/buildSrc/src/main/kotlin/CompileSkikoCppTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import kotlin.collections.HashSet

abstract class CompileSkikoCppTask() : AbstractSkikoNativeToolTask() {
@get:Internal
open val srcExtensions: Array<String> = arrayOf("cc")
open val srcExtensions: Array<String> = arrayOf("cc", "cpp")

@get:Internal
open val headerExtensions: Array<String> = arrayOf("h", "hh")
Expand Down
1 change: 1 addition & 0 deletions skiko/src/commonMain/cpp/common/include/mppinterop.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace skikoMpp {
namespace skrect {
// TODO: Combine with skija::Rect::copyToInterop
void serializeAs4Floats(const SkRect& rect, float* result);
std::unique_ptr<SkRect> toSkRect(float* topLeftRightBottom);
}
Expand Down
20 changes: 20 additions & 0 deletions skiko/src/commonMain/cpp/common/include/node/Lighting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once
#include <SkPoint3.h>

// Adoption of frameworks/base/libs/hwui/Lighting.h

namespace skiko {
namespace node {

struct LightGeometry {
SkPoint3 center;
float radius;
};

struct LightInfo {
float ambientShadowAlpha;
float spotShadowAlpha;
};

} // namespace node
} // namespace skiko
111 changes: 111 additions & 0 deletions skiko/src/commonMain/cpp/common/include/node/RenderNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#pragma once
#include <optional>
#include <SkBBHFactory.h>
#include <SkCamera.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkDrawable.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkPictureRecorder.h>
#include <SkPoint.h>
#include <SkRRect.h>
#include <SkRect.h>
#include <SkRefCnt.h>
#include "Lighting.h"

namespace skiko {
namespace node {

class RenderNodeContext;

class RenderNode : public SkDrawable {
public:
RenderNode(const sk_sp<RenderNodeContext>& context);
~RenderNode();

std::optional<SkPaint>& getLayerPaint() { return this->layerPaint; }
void setLayerPaint(const std::optional<SkPaint>& layerPaint);
const SkRect& getBounds() const { return this->bounds; }
void setBounds(const SkRect& bounds);
const SkPoint& getPivot() const { return this->pivot; }
void setPivot(const SkPoint& pivot);
float getAlpha() const { return this->alpha; }
void setAlpha(float alpha);
float getScaleX() const { return this->scaleX; }
void setScaleX(float scaleX);
float getScaleY() const { return this->scaleY; }
void setScaleY(float scaleY);
float getTranslationX() const { return this->translationX; }
void setTranslationX(float translationX);
float getTranslationY() const { return this->translationY; }
void setTranslationY(float translationY);
float getShadowElevation() const { return this->shadowElevation; }
void setShadowElevation(float shadowElevation);
SkColor getAmbientShadowColor() const { return this->ambientShadowColor; }
void setAmbientShadowColor(SkColor ambientShadowColor);
SkColor getSpotShadowColor() const { return this->spotShadowColor; }
void setSpotShadowColor(SkColor spotShadowColor);
float getRotationX() const { return this->rotationX; }
void setRotationX(float rotationX);
float getRotationY() const { return this->rotationY; }
void setRotationY(float rotationY);
float getRotationZ() const { return this->rotationZ; }
void setRotationZ(float rotationZ);
float getCameraDistance() const;
void setCameraDistance(float cameraDistance);
void setClipRect(const std::optional<SkRect>& clipRect);
void setClipRRect(const std::optional<SkRRect>& clipRRect);
void setClipPath(const std::optional<SkPath>& clipPath);
bool getClip() const { return this->clip; }
void setClip(bool clip);

const SkMatrix& getMatrix();

SkCanvas * beginRecording();
void endRecording();

void drawInto(SkCanvas *canvas);

// SkDrawable
void onDraw(SkCanvas* canvas) override;
SkRect onGetBounds() override;

protected:
sk_sp<SkPicture> onMakePictureSnapshot() override;

private:
void updateMatrix();
void drawShadow(SkCanvas *canvas, const LightGeometry& lightGeometry, const LightInfo& lightInfo);
void setCameraLocation(float x, float y, float z);

sk_sp<RenderNodeContext> context;

SkBBHFactory *bbhFactory;
SkPictureRecorder recorder;
sk_sp<SkDrawable> contentCache;

std::optional<SkPaint> layerPaint;
SkRect bounds;
SkPoint pivot;
float alpha;
float scaleX, scaleY;
float translationX, translationY;
float shadowElevation;
SkColor ambientShadowColor;
SkColor spotShadowColor;
float rotationX, rotationY, rotationZ;
std::optional<SkRect> clipRect;
std::optional<SkRRect> clipRRect;
std::optional<SkPath> clipPath;
bool clip;

SkMatrix transformMatrix;
SkCamera3D transformCamera;
bool matrixIdentity;
bool matrixDirty;
};

} // namespace node
} // namespace skiko
30 changes: 30 additions & 0 deletions skiko/src/commonMain/cpp/common/include/node/RenderNodeContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once
#include <SkRefCnt.h>
#include "Lighting.h"

namespace skiko {
namespace node {

class RenderNode;

class RenderNodeContext : public SkRefCnt {
public:
RenderNodeContext(bool measureDrawBounds);

bool shouldMeasureDrawBounds() const { return this->measureDrawBounds; }

const LightGeometry& getLightGeometry() const { return this->lightGeometry; }
const LightInfo& getLightInfo() const { return this->lightInfo; }
void setLightingInfo(
const LightGeometry& lightGeometry,
const LightInfo& lightInfo
);

private:
LightGeometry lightGeometry;
LightInfo lightInfo;
bool measureDrawBounds;
};

} // namespace node
} // namespace skiko
Loading

0 comments on commit 09dae79

Please sign in to comment.