Android: Add helper class for generating OpenGL shaders

This CL adds a helper class GlShaderBuilder to build an instances of
RendererCommon.GlDrawer that can accept multiple input sources
(OES, RGB, or YUV) using a generic fragment shader as input.

Bug: webrtc:9355
Change-Id: I14a0a280d2b6f838984f7b60897cc0c58e2a948a
Reviewed-on: https://webrtc-review.googlesource.com/80940
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23622}
This commit is contained in:
Magnus Jedvert
2018-06-15 09:33:20 +02:00
committed by Commit Bot
parent 8643b78750
commit 65c61dcfce
7 changed files with 572 additions and 414 deletions

View File

@ -0,0 +1,158 @@
/*
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.webrtc.GlShader;
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class GlGenericDrawerTest {
// Simplest possible valid generic fragment shader.
private static final String FRAGMENT_SHADER = "void main() {\n"
+ " gl_FragColor = sample(tc);\n"
+ "}\n";
private static final int TEXTURE_ID = 3;
private static final float[] TEX_MATRIX =
new float[] {1, 2, 3, 4, -1, -2, -3, -4, 0, 0, 1, 0, 0, 0, 0, 1};
private static final int FRAME_WIDTH = 640;
private static final int FRAME_HEIGHT = 480;
private static final int VIEWPORT_X = 3;
private static final int VIEWPORT_Y = 5;
private static final int VIEWPORT_WIDTH = 500;
private static final int VIEWPORT_HEIGHT = 500;
// Replace OpenGLES GlShader dependency with a mock.
private class GlGenericDrawerForTest extends GlGenericDrawer {
public GlGenericDrawerForTest(String genericFragmentSource, ShaderCallbacks shaderCallbacks) {
super(genericFragmentSource, shaderCallbacks);
}
@Override
GlShader createShader(ShaderType shaderType) {
return mockedShader;
}
}
private GlShader mockedShader;
private GlGenericDrawer glGenericDrawer;
private GlGenericDrawer.ShaderCallbacks mockedCallbacks;
@Before
public void setUp() {
mockedShader = mock(GlShader.class);
mockedCallbacks = mock(GlGenericDrawer.ShaderCallbacks.class);
glGenericDrawer = new GlGenericDrawerForTest(FRAGMENT_SHADER, mockedCallbacks);
}
@After
public void tearDown() {
verifyNoMoreInteractions(mockedCallbacks);
}
@Test
public void testOesFragmentShader() {
final String expectedOesFragmentShader = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 tc;\n"
+ "uniform samplerExternalOES tex;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(tex, tc);\n"
+ "}\n";
final String oesFragmentShader =
GlGenericDrawer.createFragmentShaderString(FRAGMENT_SHADER, GlGenericDrawer.ShaderType.OES);
assertEquals(expectedOesFragmentShader, oesFragmentShader);
}
@Test
public void testRgbFragmentShader() {
final String expectedRgbFragmentShader = "precision mediump float;\n"
+ "varying vec2 tc;\n"
+ "uniform sampler2D tex;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(tex, tc);\n"
+ "}\n";
final String rgbFragmentShader =
GlGenericDrawer.createFragmentShaderString(FRAGMENT_SHADER, GlGenericDrawer.ShaderType.RGB);
assertEquals(expectedRgbFragmentShader, rgbFragmentShader);
}
@Test
public void testYuvFragmentShader() {
final String expectedYuvFragmentShader = "precision mediump float;\n"
+ "varying vec2 tc;\n"
+ "uniform sampler2D y_tex;\n"
+ "uniform sampler2D u_tex;\n"
+ "uniform sampler2D v_tex;\n"
+ "vec4 sample(vec2 p) {\n"
+ " float y = texture2D(y_tex, p).r;\n"
+ " float u = texture2D(u_tex, p).r - 0.5;\n"
+ " float v = texture2D(v_tex, p).r - 0.5;\n"
+ " return vec4(y + 1.403 * v, y - 0.344 * u - 0.714 * v, y + 1.77 * u, 1);\n"
+ "}\n"
+ "void main() {\n"
+ " gl_FragColor = sample(tc);\n"
+ "}\n";
final String yuvFragmentShader =
GlGenericDrawer.createFragmentShaderString(FRAGMENT_SHADER, GlGenericDrawer.ShaderType.YUV);
assertEquals(expectedYuvFragmentShader, yuvFragmentShader);
}
@Test
public void testShaderCallbacksOneRgbFrame() {
glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
verify(mockedCallbacks).onNewShader(mockedShader);
verify(mockedCallbacks)
.onPrepareShader(
mockedShader, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
}
@Test
public void testShaderCallbacksTwoRgbFrames() {
glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
// Expect only one shader to be created, but two frames to be drawn.
verify(mockedCallbacks, times(1)).onNewShader(mockedShader);
verify(mockedCallbacks, times(2))
.onPrepareShader(
mockedShader, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
}
@Test
public void testShaderCallbacksChangingShaderType() {
glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
glGenericDrawer.drawOes(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
// Expect two shaders to be created, and two frames to be drawn.
verify(mockedCallbacks, times(2)).onNewShader(mockedShader);
verify(mockedCallbacks, times(2))
.onPrepareShader(
mockedShader, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
}
}