Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a277cf8c authored by Jason Sams's avatar Jason Sams Committed by Android (Google) Code Review
Browse files

Merge "Implement ScriptGroup and add test." into jb-mr1-dev

parents 77b25796 423ebcb4
Loading
Loading
Loading
Loading
+391 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.renderscript;

import java.lang.reflect.Method;

/**
 * @hide
 **/
public class ScriptGroup extends BaseObj {
    Node mNodes[];
    Connection mConnections[];
    Node mFirstNode;
    IO mOutputs[];
    IO mInputs[];

    static class IO {
        Script mScript;
        Allocation mAllocation;
        String mName;

        IO(Script s) {
            mScript = s;
        }
        IO(Script s, String n) {
            mScript = s;
            mName = n;
        }
    }

    static class Connection {
        Node mTo[];
        String mToName[];
        Node mFrom;
        Type mAllocationType;
        Allocation mInternalAllocation;

        Connection(Node out, Type t) {
            mFrom = out;
            mAllocationType = t;
        }

        void addTo(Node n, String name) {
            if (mTo == null) {
                mTo = new Node[1];
                mToName = new String[1];
            } else {
                Node nt[] = new Node[mTo.length + 1];
                String ns[] = new String[mTo.length + 1];
                System.arraycopy(mTo, 0, nt, 0, mTo.length);
                System.arraycopy(mToName, 0, ns, 0, mTo.length);
                mTo = nt;
                mToName = ns;
            }
            mTo[mTo.length - 1] = n;
            mToName[mTo.length - 1] = name;
        }
    }

    static class Node {
        Script mScript;
        Connection mInput[] = new Connection[8];
        Connection mOutput[] = new Connection[1];
        int mInputCount;
        int mOutputCount;
        int mDepth;
        boolean mSeen;

        Node mNext;

        Node(Script s) {
            mScript = s;
        }

        void addInput(Connection c) {
            if (mInput.length <= mInputCount) {
                Connection[] nc = new Connection[mInput.length + 8];
                System.arraycopy(mInput, 0, nc, 0, mInputCount);
                mInput = nc;
            }
            mInput[mInputCount++] = c;
        }

        void addOutput(Connection c) {
            if (mOutput.length <= mOutputCount) {
                Connection[] nc = new Connection[mOutput.length + 8];
                System.arraycopy(mOutput, 0, nc, 0, mOutputCount);
                mOutput = nc;
            }
            mOutput[mOutputCount++] = c;
        }
    }


    ScriptGroup(int id, RenderScript rs) {
        super(id, rs);
    }

    void init(int nodeCount, int connectionCount) {
        mNodes = new Node[nodeCount];
        mConnections = new Connection[connectionCount];

        android.util.Log.v("RSR", "init" + nodeCount + ", " + connectionCount);

        // Count outputs and create array.
        Node n = mFirstNode;
        int outputCount = 0;
        int inputCount = 0;
        int connectionIndex = 0;
        int nodeNum = 0;
        while (n != null) {
            mNodes[nodeNum++] = n;

            // Look for unattached kernel inputs
            boolean hasInput = false;
            for (int ct=0; ct < n.mInput.length; ct++) {
                if (n.mInput[ct] != null) {
                    if (n.mInput[ct].mToName == null) {
                        hasInput = true;
                    }
                }
            }
            if (!hasInput) {
                if (mInputs == null) {
                    mInputs = new IO[1];
                }
                if (mInputs.length <= inputCount) {
                    IO t[] = new IO[mInputs.length + 1];
                    System.arraycopy(mInputs, 0, t, 0, mInputs.length);
                    mInputs = t;
                }
                mInputs[inputCount++] = new IO(n.mScript);
            }

            // Look for unattached kernel outputs
            boolean hasOutput = false;
            for (int ct=0; ct < n.mOutput.length; ct++) {
                if (n.mOutput[ct] != null) {
                    hasOutput = true;
                }
            }
            if (!hasOutput) {
                if (mOutputs == null) {
                    mOutputs = new IO[1];
                }
                if (mOutputs.length <= outputCount) {
                    IO t[] = new IO[mOutputs.length + 1];
                    System.arraycopy(mOutputs, 0, t, 0, mOutputs.length);
                    mOutputs = t;
                }
                mOutputs[outputCount++] = new IO(n.mScript);
            }

            // Make allocations for internal connections
            // Since script outputs are unique, use those to avoid duplicates.
            for (int ct=0; ct < n.mOutput.length; ct++) {
                android.util.Log.v("RSR", "init out2 " + n.mOutput[ct]);
                if (n.mOutput[ct] != null) {
                    Connection t = n.mOutput[ct];
                    mConnections[connectionIndex++] = t;
                    t.mInternalAllocation = Allocation.createTyped(mRS, t.mAllocationType);
                }
            }

            n = n.mNext;
        }
    }

    public void setInput(Script s, Allocation a) {
        for (int ct=0; ct < mInputs.length; ct++) {
            if (mInputs[ct].mScript == s) {
                mInputs[ct].mAllocation = a;
                return;
            }
        }
        throw new RSIllegalArgumentException("Script not found");
    }

    public void setOutput(Script s, Allocation a) {
        for (int ct=0; ct < mOutputs.length; ct++) {
            if (mOutputs[ct].mScript == s) {
                mOutputs[ct].mAllocation = a;
                return;
            }
        }
        throw new RSIllegalArgumentException("Script not found");
    }

    public void execute() {
        android.util.Log.v("RSR", "execute");
        boolean more = true;
        int depth = 0;
        while (more) {
            more = false;
            for (int ct=0; ct < mNodes.length; ct++) {
                if (mNodes[ct].mDepth == depth) {
                    more = true;

                    Allocation kernelIn = null;
                    for (int ct2=0; ct2 < mNodes[ct].mInputCount; ct2++) {
                        android.util.Log.v("RSR", " kin " + ct2 + ", to " + mNodes[ct].mInput[ct2].mTo[0] + ", name " + mNodes[ct].mInput[ct2].mToName[0]);
                        if (mNodes[ct].mInput[ct2].mToName[0] == null) {
                            kernelIn = mNodes[ct].mInput[ct2].mInternalAllocation;
                            break;
                        }
                    }

                    Allocation kernelOut= null;
                    for (int ct2=0; ct2 < mNodes[ct].mOutputCount; ct2++) {
                        android.util.Log.v("RSR", " kout " + ct2 + ", from " + mNodes[ct].mOutput[ct2].mFrom);
                        if (mNodes[ct].mOutput[ct2].mFrom != null) {
                            kernelOut = mNodes[ct].mOutput[ct2].mInternalAllocation;
                            break;
                        }
                    }
                    if (kernelOut == null) {
                        for (int ct2=0; ct2 < mOutputs.length; ct2++) {
                            if (mOutputs[ct2].mScript == mNodes[ct].mScript) {
                                kernelOut = mOutputs[ct2].mAllocation;
                                break;
                            }
                        }
                    }

                    android.util.Log.v("RSR", "execute calling " + mNodes[ct] + ", with " + kernelIn);
                    if (kernelIn != null) {
                        try {

                            Method m = mNodes[ct].mScript.getClass().getMethod("forEach_root",
                                          new Class[] { Allocation.class, Allocation.class });
                            m.invoke(mNodes[ct].mScript, new Object[] {kernelIn, kernelOut} );
                        } catch (Throwable t) {
                            android.util.Log.e("RSR", "execute error " + t);
                        }
                    } else {
                        try {
                            Method m = mNodes[ct].mScript.getClass().getMethod("forEach_root",
                                          new Class[] { Allocation.class });
                            m.invoke(mNodes[ct].mScript, new Object[] {kernelOut} );
                        } catch (Throwable t) {
                            android.util.Log.e("RSR", "execute error " + t);
                        }
                    }

                }
            }
            depth ++;
        }

    }


    public static class Builder {
        RenderScript mRS;
        Node mFirstNode;
        int mConnectionCount = 0;
        int mNodeCount = 0;

        public Builder(RenderScript rs) {
            mRS = rs;
        }

        private void validateRecurse(Node n, int depth) {
            n.mSeen = true;
            if (depth > n.mDepth) {
                n.mDepth = depth;
            }

            android.util.Log.v("RSR", " validateRecurse outputCount " + n.mOutputCount);
            for (int ct=0; ct < n.mOutputCount; ct++) {
                for (int ct2=0; ct2 < n.mOutput[ct].mTo.length; ct2++) {
                    if (n.mOutput[ct].mTo[ct2].mSeen) {
                        throw new RSInvalidStateException("Loops in group not allowed.");
                    }
                    validateRecurse(n.mOutput[ct].mTo[ct2], depth + 1);
                }
            }
        }

        private void validate() {
            android.util.Log.v("RSR", "validate");
            Node n = mFirstNode;
            while (n != null) {
                n.mSeen = false;
                n.mDepth = 0;
                n = n.mNext;
            }

            n = mFirstNode;
            while (n != null) {
                android.util.Log.v("RSR", "validate n= " + n);
                if ((n.mSeen == false) && (n.mInputCount == 0)) {
                    android.util.Log.v("RSR", " recursing " + n);
                    validateRecurse(n, 0);
                }
                n = n.mNext;
            }
        }

        private Node findScript(Script s) {
            Node n = mFirstNode;
            while (n != null) {
                if (n.mScript == s) {
                    return n;
                }
                n = n.mNext;
            }
            return null;
        }

        private void addNode(Node n) {
            n.mNext = mFirstNode;
            mFirstNode = n;
        }

        public Builder addConnection(Type t, Script output, Script input, String inputName) {
            android.util.Log.v("RSR", "addConnection " + t +", " + output + ", " + input);

            // Look for existing output
            Node nout = findScript(output);
            Connection c;
            if (nout == null) {
                // Make new node
                android.util.Log.v("RSR", "addConnection new output node");
                nout = new Node(output);
                mNodeCount++;
                c = new Connection(nout, t);
                mConnectionCount++;
                nout.addOutput(c);
                addNode(nout);
            } else {
                // Add to existing node
                android.util.Log.v("RSR", "addConnection reuse output node");
                if (nout.mOutput[0] != null) {
                    if (nout.mOutput[0].mFrom.mScript != output) {
                        throw new RSInvalidStateException("Changed output of existing node");
                    }
                    if (nout.mOutput[0].mAllocationType != t) {
                        throw new RSInvalidStateException("Changed output type of existing node");
                    }
                }
                c = nout.mOutput[0];
            }
            // At this point we should have a connection attached to a script ouput.

            // Find input
            Node nin = findScript(input);
            if (nin == null) {
                android.util.Log.v("RSR", "addConnection new input node");
                nin = new Node(input);
                mNodeCount++;
                addNode(nin);
            }
            c.addTo(nin, inputName);
            nin.addInput(c);

            validate();
            return this;
        }

        public ScriptGroup create() {
            ScriptGroup sg = new ScriptGroup(0, mRS);
            sg.mFirstNode = mFirstNode;
            mFirstNode = null;

            android.util.Log.v("RSR", "create nodes= " + mNodeCount + ", Connections= " + mConnectionCount);

            sg.init(mNodeCount, mConnectionCount);
            return sg;
        }

    }


}

+94 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.rs.image;

import java.lang.Math;

import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.Script;
import android.renderscript.ScriptC;
import android.renderscript.Type;
import android.renderscript.Matrix4f;
import android.renderscript.ScriptGroup;
import android.util.Log;

public class GroupTest extends TestBase {
    private ScriptC_convolve3x3 mConvolve;
    private ScriptC_colormatrix mMatrix;

    private Allocation mScratchPixelsAllocation1;
    private ScriptGroup mGroup;

    private int mWidth;
    private int mHeight;
    private boolean mUseNative;


    public GroupTest(boolean useNative) {
        mUseNative = useNative;
    }

    public void createTest(android.content.res.Resources res) {
        mWidth = mInPixelsAllocation.getType().getX();
        mHeight = mInPixelsAllocation.getType().getY();

        mConvolve = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
        mMatrix = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);

        float f[] = new float[9];
        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
        mConvolve.set_gCoeffs(f);

        Matrix4f m = new Matrix4f();
        m.set(1, 0, 0.2f);
        m.set(1, 1, 0.9f);
        m.set(1, 2, 0.2f);
        mMatrix.invoke_setMatrix(m);

        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
        tb.setX(mWidth);
        tb.setY(mHeight);
        Type connect = tb.create();

        if (mUseNative) {
            ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
            b.addConnection(connect, mConvolve, mMatrix, null);
            mGroup = b.create();

        } else {
            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, connect);
        }
    }

    public void runTest() {
        mConvolve.set_gIn(mInPixelsAllocation);
        mConvolve.set_gWidth(mWidth);
        mConvolve.set_gHeight(mHeight);
        if (mUseNative) {
            mGroup.setOutput(mMatrix, mOutPixelsAllocation);
            mGroup.execute();
        } else {
            mConvolve.forEach_root(mScratchPixelsAllocation1);
            mMatrix.forEach_root(mScratchPixelsAllocation1, mOutPixelsAllocation);
        }
    }

}
+9 −1
Original line number Diff line number Diff line
@@ -161,6 +161,12 @@ public class ImageProcessingActivity extends Activity
        case 12:
            mTest = new Vignette(true, true);
            break;
        case 13:
            mTest = new GroupTest(true);
            break;
        case 14:
            mTest = new GroupTest(false);
            break;
        }

        mTest.createBaseTest(this, mBitmapIn);
@@ -173,7 +179,7 @@ public class ImageProcessingActivity extends Activity
    }

    void setupTests() {
        mTestNames = new String[13];
        mTestNames = new String[15];
        mTestNames[0] = "Levels Vec3 Relaxed";
        mTestNames[1] = "Levels Vec4 Relaxed";
        mTestNames[2] = "Levels Vec3 Full";
@@ -187,6 +193,8 @@ public class ImageProcessingActivity extends Activity
        mTestNames[10] = "Vignette Relaxed";
        mTestNames[11] = "Vignette Approximate Full";
        mTestNames[12] = "Vignette Approximate Relaxed";
        mTestNames[13] = "Group Test (emulated)";
        mTestNames[14] = "Group Test (native)";
        mTestSpinner.setAdapter(new ArrayAdapter<String>(
            this, R.layout.spinner_layout, mTestNames));
    }
+38 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma version(1)
#pragma rs java_package_name(com.android.rs.image)
#pragma rs_fp_relaxed


static rs_matrix4x4 Mat;

void init() {
    rsMatrixLoadIdentity(&Mat);
}

void setMatrix(rs_matrix4x4 m) {
    Mat = m;
}

void root(const uchar4 *in, uchar4 *out) {
    float4 f = convert_float4(*in);
    f = rsMatrixMultiply(&Mat, f);
    f = clamp(f, 0.f, 255.f);
    *out = convert_uchar4(f);
}
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma version(1)
#pragma rs java_package_name(com.android.rs.image)
#pragma rs_fp_relaxed

int32_t gWidth;
int32_t gHeight;
rs_allocation gIn;

float gCoeffs[9] ;
void setCoefficients(float coef[9]) {
    for(int i=0; i < 9; i++) {
        gCoeffs[i] = coef[i];
    }
}

void root(uchar4 *out, uint32_t x, uint32_t y) {
    uint32_t x1 = min((int32_t)x+1, gWidth);
    uint32_t x2 = max((int32_t)x-1, 0);
    uint32_t y1 = min((int32_t)y+1, gHeight);
    uint32_t y2 = max((int32_t)y-1, 0);

    float4 p00 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]);
    float4 p01 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y1))[0]);
    float4 p02 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y1))[0]);
    float4 p10 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y))[0]);
    float4 p11 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y))[0]);
    float4 p12 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y))[0]);
    float4 p20 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y2))[0]);
    float4 p21 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y2))[0]);
    float4 p22 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y2))[0]);
    p00 *= gCoeffs[0];
    p01 *= gCoeffs[1];
    p02 *= gCoeffs[2];
    p10 *= gCoeffs[3];
    p11 *= gCoeffs[4];
    p12 *= gCoeffs[5];
    p20 *= gCoeffs[6];
    p21 *= gCoeffs[7];
    p22 *= gCoeffs[8];

    p00 += p01;
    p02 += p10;
    p11 += p12;
    p20 += p21;

    p22 += p00;
    p02 += p11;

    p20 += p22;
    p20 += p02;

    p20 = clamp(p20, 0.f, 255.f);
    *out = convert_uchar4(p20);
}