RecordedOp.h revision d3daa3198e2212c985c634821682d5819346b653
1b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik/*
2b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * Copyright (C) 2015 The Android Open Source Project
3b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik *
4b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * Licensed under the Apache License, Version 2.0 (the "License");
5b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * you may not use this file except in compliance with the License.
6b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * You may obtain a copy of the License at
7b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik *
8b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik *      http://www.apache.org/licenses/LICENSE-2.0
9b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik *
10b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * Unless required by applicable law or agreed to in writing, software
11b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * distributed under the License is distributed on an "AS IS" BASIS,
12b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * See the License for the specific language governing permissions and
14b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * limitations under the License.
15b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik */
16b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
17b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#ifndef ANDROID_HWUI_RECORDED_OP_H
18b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define ANDROID_HWUI_RECORDED_OP_H
19b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
20b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#include "utils/LinearAllocator.h"
21b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#include "Rect.h"
22b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#include "Matrix.h"
230b7e8245db728d127ada698be63d78b33fc6e4daChris Craik#include "RenderNode.h"
24b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
25b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#include "SkXfermode.h"
26b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
27b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikclass SkBitmap;
28b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikclass SkPaint;
29b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
30b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craiknamespace android {
31b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craiknamespace uirenderer {
32b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
335854b34881b1a747ac80b5077869ef270a92b1f4Chris Craikclass OffscreenBuffer;
34b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikclass RenderNode;
35b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstruct Vertex;
36b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
37b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik/**
38b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * The provided macro is executed for each op type in order, with the results separated by commas.
39b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik *
40b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik * This serves as the authoritative list of ops, used for generating ID enum, and ID based LUTs.
41b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik */
42b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define MAP_OPS(OP_FN) \
43b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik        OP_FN(BitmapOp) \
44b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik        OP_FN(RectOp) \
45b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik        OP_FN(RenderNodeOp) \
46d3daa3198e2212c985c634821682d5819346b653Chris Craik        OP_FN(ShadowOp) \
476fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik        OP_FN(SimpleRectsOp) \
486fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik        OP_FN(BeginLayerOp) \
496fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik        OP_FN(EndLayerOp) \
506fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik        OP_FN(LayerOp)
51b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
52b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik// Generate OpId enum
53b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define IDENTITY_FN(Type) Type,
54b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craiknamespace RecordedOpId {
55b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    enum {
56b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik        MAP_OPS(IDENTITY_FN)
57b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik        Count,
58b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    };
59b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik}
60b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstatic_assert(RecordedOpId::BitmapOp == 0,
61b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik        "First index must be zero for LUTs to work");
62b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
63b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint
64b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect
65b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, paint)
66b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, nullptr)
67b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
68b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstruct RecordedOp {
69b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    /* ID from RecordedOpId - generally used for jumping into function tables */
70b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const int opId;
71b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
72b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    /* bounds in *local* space, without accounting for DisplayList transformation */
73b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const Rect unmappedBounds;
74b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
75b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    /* transform in recording space (vs DisplayList origin) */
76b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const Matrix4 localMatrix;
77b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
78b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    /* clip in recording space */
79b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const Rect localClipRect;
80b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
81b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    /* optional paint, stored in base object to simplify merging logic */
82b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const SkPaint* paint;
83b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikprotected:
84b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    RecordedOp(unsigned int opId, BASE_PARAMS)
85b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            : opId(opId)
86b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , unmappedBounds(unmappedBounds)
87b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , localMatrix(localMatrix)
88b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , localClipRect(localClipRect)
89b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , paint(paint) {}
90b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik};
91b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
92b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstruct RenderNodeOp : RecordedOp {
93b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
94b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            : SUPER_PAINTLESS(RenderNodeOp)
95b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , renderNode(renderNode) {}
96b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    RenderNode * renderNode; // not const, since drawing modifies it (somehow...)
97161f54b2d4160b8d3f3da9eba5746da5162e4821Chris Craik    bool skipInOrderDraw = false;
98b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik};
99b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
100b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstruct BitmapOp : RecordedOp {
101b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
102b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            : SUPER(BitmapOp)
103b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , bitmap(bitmap) {}
104b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const SkBitmap* bitmap;
105b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    // TODO: asset atlas/texture id lookup?
106b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik};
107b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
108b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstruct RectOp : RecordedOp {
109b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    RectOp(BASE_PARAMS)
110b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            : SUPER(RectOp) {}
111b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik};
112b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
113d3daa3198e2212c985c634821682d5819346b653Chris Craik/**
114d3daa3198e2212c985c634821682d5819346b653Chris Craik * Real-time, dynamic-lit shadow.
115d3daa3198e2212c985c634821682d5819346b653Chris Craik *
116d3daa3198e2212c985c634821682d5819346b653Chris Craik * Uses invalid/empty bounds and matrix since ShadowOp bounds aren't known at defer time,
117d3daa3198e2212c985c634821682d5819346b653Chris Craik * and are resolved dynamically, and transform isn't needed.
118d3daa3198e2212c985c634821682d5819346b653Chris Craik *
119d3daa3198e2212c985c634821682d5819346b653Chris Craik * State construction handles these properties specially.
120d3daa3198e2212c985c634821682d5819346b653Chris Craik */
121d3daa3198e2212c985c634821682d5819346b653Chris Craikstruct ShadowOp : RecordedOp {
122d3daa3198e2212c985c634821682d5819346b653Chris Craik    ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath, const Rect& clipRect)
123d3daa3198e2212c985c634821682d5819346b653Chris Craik            : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), clipRect, nullptr)
124d3daa3198e2212c985c634821682d5819346b653Chris Craik            , shadowMatrixXY(casterOp.localMatrix)
125d3daa3198e2212c985c634821682d5819346b653Chris Craik            , shadowMatrixZ(casterOp.localMatrix)
126d3daa3198e2212c985c634821682d5819346b653Chris Craik            , casterAlpha(casterAlpha)
127d3daa3198e2212c985c634821682d5819346b653Chris Craik            , casterPath(casterPath) {
128d3daa3198e2212c985c634821682d5819346b653Chris Craik        const RenderNode& node = *casterOp.renderNode;
129d3daa3198e2212c985c634821682d5819346b653Chris Craik        node.applyViewPropertyTransforms(shadowMatrixXY, false);
130d3daa3198e2212c985c634821682d5819346b653Chris Craik        node.applyViewPropertyTransforms(shadowMatrixZ, true);
131d3daa3198e2212c985c634821682d5819346b653Chris Craik    };
132d3daa3198e2212c985c634821682d5819346b653Chris Craik    Matrix4 shadowMatrixXY;
133d3daa3198e2212c985c634821682d5819346b653Chris Craik    Matrix4 shadowMatrixZ;
134d3daa3198e2212c985c634821682d5819346b653Chris Craik    const float casterAlpha;
135d3daa3198e2212c985c634821682d5819346b653Chris Craik    const SkPath* casterPath;
136d3daa3198e2212c985c634821682d5819346b653Chris Craik};
137d3daa3198e2212c985c634821682d5819346b653Chris Craik
138b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craikstruct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
139b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount)
140b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            : SUPER(SimpleRectsOp)
141b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , vertices(vertices)
142b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik            , vertexCount(vertexCount) {}
143b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    Vertex* vertices;
144b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik    const size_t vertexCount;
145b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik};
146b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
1476fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik/**
1486fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik * Stateful operation! denotes the creation of an off-screen layer,
1496fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik * and that commands following will render into it.
1506fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik */
1516fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craikstruct BeginLayerOp : RecordedOp {
1526fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik    BeginLayerOp(BASE_PARAMS)
1536fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik            : SUPER(BeginLayerOp) {}
1546fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik};
1556fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik
1566fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik/**
1576fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik * Stateful operation! Denotes end of off-screen layer, and that
1586fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik * commands since last BeginLayerOp should be drawn into parent FBO.
1596fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik *
1606fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik * State in this op is empty, it just serves to signal that a layer has been finished.
1616fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik */
1626fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craikstruct EndLayerOp : RecordedOp {
1636fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik    EndLayerOp()
1646fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik            : RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
1656fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik};
1666fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik
1670b7e8245db728d127ada698be63d78b33fc6e4daChris Craik/**
1680b7e8245db728d127ada698be63d78b33fc6e4daChris Craik * Draws an OffscreenBuffer.
1690b7e8245db728d127ada698be63d78b33fc6e4daChris Craik *
1700b7e8245db728d127ada698be63d78b33fc6e4daChris Craik * Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated,
1710b7e8245db728d127ada698be63d78b33fc6e4daChris Craik * when creating/tracking a SkPaint* during defer isn't worth the bother.
1720b7e8245db728d127ada698be63d78b33fc6e4daChris Craik */
1736fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craikstruct LayerOp : RecordedOp {
1740b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    // Records a one-use (saveLayer) layer for drawing. Once drawn, the layer will be destroyed.
1755854b34881b1a747ac80b5077869ef270a92b1f4Chris Craik    LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
1760b7e8245db728d127ada698be63d78b33fc6e4daChris Craik            : SUPER_PAINTLESS(LayerOp)
1770b7e8245db728d127ada698be63d78b33fc6e4daChris Craik            , layerHandle(layerHandle)
1780b7e8245db728d127ada698be63d78b33fc6e4daChris Craik            , alpha(paint->getAlpha() / 255.0f)
1790b7e8245db728d127ada698be63d78b33fc6e4daChris Craik            , mode(PaintUtils::getXfermodeDirect(paint))
1800b7e8245db728d127ada698be63d78b33fc6e4daChris Craik            , colorFilter(paint->getColorFilter())
1810b7e8245db728d127ada698be63d78b33fc6e4daChris Craik            , destroy(true) {}
1820b7e8245db728d127ada698be63d78b33fc6e4daChris Craik
1830b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    LayerOp(RenderNode& node)
1840b7e8245db728d127ada698be63d78b33fc6e4daChris Craik        : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr)
1850b7e8245db728d127ada698be63d78b33fc6e4daChris Craik        , layerHandle(node.getLayerHandle())
1860b7e8245db728d127ada698be63d78b33fc6e4daChris Craik        , alpha(node.properties().layerProperties().alpha() / 255.0f)
1870b7e8245db728d127ada698be63d78b33fc6e4daChris Craik        , mode(node.properties().layerProperties().xferMode())
1880b7e8245db728d127ada698be63d78b33fc6e4daChris Craik        , colorFilter(node.properties().layerProperties().colorFilter())
1890b7e8245db728d127ada698be63d78b33fc6e4daChris Craik        , destroy(false) {}
1900b7e8245db728d127ada698be63d78b33fc6e4daChris Craik
191818c9fbf1d76d5df19253ba4eb964efa939ec9ecChris Craik    // Records a handle to the Layer object, since the Layer itself won't be
192818c9fbf1d76d5df19253ba4eb964efa939ec9ecChris Craik    // constructed until after this operation is constructed.
1935854b34881b1a747ac80b5077869ef270a92b1f4Chris Craik    OffscreenBuffer** layerHandle;
1940b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    const float alpha;
1950b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    const SkXfermode::Mode mode;
1960b7e8245db728d127ada698be63d78b33fc6e4daChris Craik
1970b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    // pointer to object owned by either LayerProperties, or a recorded Paint object in a
1980b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    // BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
1990b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    SkColorFilter* colorFilter;
2000b7e8245db728d127ada698be63d78b33fc6e4daChris Craik
2010b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    // whether to destroy the layer, once rendered
2020b7e8245db728d127ada698be63d78b33fc6e4daChris Craik    const bool destroy;
2036fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik};
2046fe991e5e76f9af9dab960100d5768d96d5f4daaChris Craik
205b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik}; // namespace uirenderer
206b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik}; // namespace android
207b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik
208b565df13a9e5c7b1d7d93bdfa4a793752d66d3ccChris Craik#endif // ANDROID_HWUI_RECORDED_OP_H
209