diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-01 19:31:52 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-01 19:31:52 +0000 |
commit | 7b923c6547a3619e4f0727d98e4b882c4d06eb77 (patch) | |
tree | 14fa82f6fe8a55659ec1befd1832062a3a05144e /media | |
parent | 92ccbddc2fff4fe5afd5b7552f419adfa9d3b877 (diff) | |
download | chromium_src-7b923c6547a3619e4f0727d98e4b882c4d06eb77.zip chromium_src-7b923c6547a3619e4f0727d98e4b882c4d06eb77.tar.gz chromium_src-7b923c6547a3619e4f0727d98e4b882c4d06eb77.tar.bz2 |
Benchmark tool for GPU-accelerated video rendering
This is a benchmarking program that times how fast a shader
can color-convert and render video frames to the screen. The
program takes a file with raw YUV frames, the number of
frames in the file, and the dimensions of the video file as
parameters and outputs the max frames per second obtained
when rendering the video using various painting/shading
techniques.
BUG=48633
TEST=compiles
Review URL: http://codereview.chromium.org/4873002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67887 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/media.gyp | 46 | ||||
-rw-r--r-- | media/tools/shader_bench/cpu_color_painter.cc | 92 | ||||
-rw-r--r-- | media/tools/shader_bench/cpu_color_painter.h | 32 | ||||
-rw-r--r-- | media/tools/shader_bench/gpu_color_painter.cc | 117 | ||||
-rw-r--r-- | media/tools/shader_bench/gpu_color_painter.h | 34 | ||||
-rw-r--r-- | media/tools/shader_bench/gpu_color_painter_exp.cc | 130 | ||||
-rw-r--r-- | media/tools/shader_bench/gpu_color_painter_exp.h | 35 | ||||
-rw-r--r-- | media/tools/shader_bench/gpu_painter.cc | 89 | ||||
-rw-r--r-- | media/tools/shader_bench/gpu_painter.h | 42 | ||||
-rw-r--r-- | media/tools/shader_bench/painter.cc | 26 | ||||
-rw-r--r-- | media/tools/shader_bench/painter.h | 39 | ||||
-rw-r--r-- | media/tools/shader_bench/shader_bench.cc | 163 | ||||
-rw-r--r-- | media/tools/shader_bench/window.cc | 20 | ||||
-rw-r--r-- | media/tools/shader_bench/window.h | 60 | ||||
-rw-r--r-- | media/tools/shader_bench/window_linux.cc | 89 | ||||
-rw-r--r-- | media/tools/shader_bench/window_win.cc | 139 |
16 files changed, 1153 insertions, 0 deletions
diff --git a/media/media.gyp b/media/media.gyp index 110bf92..bbd418d 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -465,6 +465,52 @@ }, ], }], + ['OS!="mac"', { + 'targets': [ + { + 'target_name': 'shader_bench', + 'type': 'executable', + 'dependencies': [ + 'media', + '../app/app.gyp:app_base', + ], + 'sources': [ + 'tools/shader_bench/shader_bench.cc', + 'tools/shader_bench/cpu_color_painter.cc', + 'tools/shader_bench/cpu_color_painter.h', + 'tools/shader_bench/gpu_color_painter.cc', + 'tools/shader_bench/gpu_color_painter.h', + 'tools/shader_bench/gpu_color_painter_exp.cc', + 'tools/shader_bench/gpu_color_painter_exp.h', + 'tools/shader_bench/gpu_painter.cc', + 'tools/shader_bench/gpu_painter.h', + 'tools/shader_bench/painter.cc', + 'tools/shader_bench/painter.h', + 'tools/shader_bench/window.cc', + 'tools/shader_bench/window.h', + ], + 'conditions': [ + ['OS=="linux"', { + 'dependencies': [ + '../build/linux/system.gyp:gtk', + ], + 'sources': [ + 'tools/shader_bench/window_linux.cc', + ], + }], + ['OS=="win"', { + 'dependencies': [ + '../third_party/angle/src/build_angle.gyp:libEGL', + '../third_party/angle/src/build_angle.gyp:libGLESv2', + ], + 'sources': [ + 'tools/shader_bench/window_win.cc', + ], + }], + ], + }, + ], + }], ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { 'targets': [ { diff --git a/media/tools/shader_bench/cpu_color_painter.cc b/media/tools/shader_bench/cpu_color_painter.cc new file mode 100644 index 0000000..61347ea --- /dev/null +++ b/media/tools/shader_bench/cpu_color_painter.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/yuv_convert.h" +#include "media/tools/shader_bench/cpu_color_painter.h" + +// Pass-through vertex shader. +static const char kVertexShader[] = + "precision highp float;\n" + "precision highp int;\n" + "varying vec2 interp_tc;\n" + "\n" + "attribute vec4 in_pos;\n" + "attribute vec2 in_tc;\n" + "\n" + "void main() {\n" + " interp_tc = in_tc;\n" + " gl_Position = in_pos;\n" + "}\n"; + +// RGB pixel shader. +static const char kFragmentShader[] = + "precision mediump float;\n" + "precision mediump int;\n" + "varying vec2 interp_tc;\n" + "\n" + "uniform sampler2D rgba_tex;\n" + "\n" + "void main() {\n" + " vec4 texColor = texture2D(rgba_tex, interp_tc);" + " gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w);\n" + "}\n"; + +CPUColorPainter::CPUColorPainter() + : program_id_(-1) { +} + +CPUColorPainter::~CPUColorPainter() { + if (program_id_) { + glDeleteProgram(program_id_); + glDeleteTextures(media::VideoFrame::kNumRGBPlanes, textures_); + } +} + +void CPUColorPainter::Initialize(int width, int height) { + glGenTextures(media::VideoFrame::kNumRGBPlanes, textures_); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures_[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); + + GLuint program = CreateShaderProgram(kVertexShader, kFragmentShader); + + // Bind parameters. + glUniform1i(glGetUniformLocation(program, "rgba_tex"), 0); + program_id_ = program; +} + +void CPUColorPainter::Paint(scoped_refptr<media::VideoFrame> video_frame) { + // Convert to RGBA frame. + scoped_refptr<media::VideoFrame> rgba_frame; + media::VideoFrame::CreateFrame(media::VideoFrame::RGBA, + video_frame->width(), + video_frame->height(), + base::TimeDelta(), + base::TimeDelta(), + &rgba_frame); + + media::ConvertYUVToRGB32(video_frame->data(media::VideoFrame::kYPlane), + video_frame->data(media::VideoFrame::kUPlane), + video_frame->data(media::VideoFrame::kVPlane), + rgba_frame->data(0), + video_frame->width(), + video_frame->height(), + video_frame->stride(media::VideoFrame::kYPlane), + video_frame->stride(media::VideoFrame::kUPlane), + rgba_frame->stride(0), + media::YV12); + + glBindTexture(GL_TEXTURE_2D, textures_[0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgba_frame->width(), + rgba_frame->height(), GL_RGBA, GL_UNSIGNED_BYTE, + rgba_frame->data(0)); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + context()->SwapBuffers(); +} diff --git a/media/tools/shader_bench/cpu_color_painter.h b/media/tools/shader_bench/cpu_color_painter.h new file mode 100644 index 0000000..c3e14c4 --- /dev/null +++ b/media/tools/shader_bench/cpu_color_painter.h @@ -0,0 +1,32 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_TOOLS_SHADER_BENCH_CPU_COLOR_PAINTER_H_ +#define MEDIA_TOOLS_SHADER_BENCH_CPU_COLOR_PAINTER_H_ + +#include "base/scoped_ptr.h" +#include "media/base/video_frame.h" +#include "media/tools/shader_bench/gpu_painter.h" + +// Does color conversion using CPU, rendering on GPU. +class CPUColorPainter : public GPUPainter { + public: + CPUColorPainter(); + virtual ~CPUColorPainter(); + + // Painter interface. + virtual void Initialize(int width, int height); + virtual void Paint(scoped_refptr<media::VideoFrame> video_frame); + + private: + // Shader program id. + GLuint program_id_; + + // ID of rgba texture. + GLuint textures_[1]; + + DISALLOW_COPY_AND_ASSIGN(CPUColorPainter); +}; + +#endif // MEDIA_TOOLS_SHADER_BENCH_CPU_COLOR_PAINTER_H_ diff --git a/media/tools/shader_bench/gpu_color_painter.cc b/media/tools/shader_bench/gpu_color_painter.cc new file mode 100644 index 0000000..b02160d --- /dev/null +++ b/media/tools/shader_bench/gpu_color_painter.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "app/gfx/gl/gl_context.h" +#include "media/tools/shader_bench/gpu_color_painter.h" + +// Matrix used for the YUV to RGB conversion. +static const float kYUV2RGB[9] = { + 1.f, 0.f, 1.403f, + 1.f, -.344f, -.714f, + 1.f, 1.772f, 0.f, +}; + +static const float kYUV2RGB_TRANS[9] = { + 1.f, 1.f, 1.f, + 0.f, -.344f, 1.772f, + 1.403f, -.714f, 0.f, +}; + +// Pass-through vertex shader. +static const char kVertexShader[] = + "precision highp float;\n" + "precision highp int;\n" + "varying vec2 interp_tc;\n" + "\n" + "attribute vec4 in_pos;\n" + "attribute vec2 in_tc;\n" + "\n" + "void main() {\n" + " interp_tc = in_tc;\n" + " gl_Position = in_pos;\n" + "}\n"; + +// YUV to RGB pixel shader. Loads a pixel from each plane and pass through the +// matrix. +static const char kFragmentShader[] = + "precision mediump float;\n" + "precision mediump int;\n" + "varying vec2 interp_tc;\n" + "\n" + "uniform sampler2D y_tex;\n" + "uniform sampler2D u_tex;\n" + "uniform sampler2D v_tex;\n" + "uniform mat3 yuv2rgb;\n" + "\n" + "void main() {\n" + " float y = texture2D(y_tex, interp_tc).x;\n" + " float u = texture2D(u_tex, interp_tc).r - .5;\n" + " float v = texture2D(v_tex, interp_tc).r - .5;\n" + " vec3 rgb = yuv2rgb * vec3(y, u, v);\n" + " gl_FragColor = vec4(rgb, 1);\n" + "}\n"; + +GPUColorWithLuminancePainter::GPUColorWithLuminancePainter() + : program_id_(-1) { +} + +GPUColorWithLuminancePainter::~GPUColorWithLuminancePainter() { + if (program_id_) { + glDeleteProgram(program_id_); + glDeleteTextures(media::VideoFrame::kNumYUVPlanes, textures_); + } +} + +void GPUColorWithLuminancePainter::Initialize(int width, int height) { + // Create 3 textures, one for each plane, and bind them to different + // texture units. + glGenTextures(media::VideoFrame::kNumYUVPlanes, textures_); + + for (unsigned int i = 0; i < media::VideoFrame::kNumYUVPlanes; ++i) { + unsigned int texture_width = (i == media::VideoFrame::kYPlane) ? + width : width / 2; + unsigned int texture_height = (i == media::VideoFrame::kYPlane) ? + height : height / 2; + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, textures_[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, texture_width, texture_height, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); + } + + GLuint program = CreateShaderProgram(kVertexShader, kFragmentShader); + + // Bind parameters. + glUniform1i(glGetUniformLocation(program, "y_tex"), 0); + glUniform1i(glGetUniformLocation(program, "u_tex"), 1); + glUniform1i(glGetUniformLocation(program, "v_tex"), 2); + int yuv2rgb_location = glGetUniformLocation(program, "yuv2rgb"); + + // DesktopGL supports transpose matrices. + if (gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) + glUniformMatrix3fv(yuv2rgb_location, 1, GL_TRUE, kYUV2RGB); + else + glUniformMatrix3fv(yuv2rgb_location, 1, GL_FALSE, kYUV2RGB_TRANS); + + program_id_ = program; +} + +void GPUColorWithLuminancePainter::Paint( + scoped_refptr<media::VideoFrame> video_frame) { + for (unsigned int i = 0; i < media::VideoFrame::kNumYUVPlanes; ++i) { + unsigned int width = (i == media::VideoFrame::kYPlane) ? + video_frame->width() : video_frame->width() / 2; + unsigned int height = (i == media::VideoFrame::kYPlane) ? + video_frame->height() : video_frame->height() / 2; + glBindTexture(GL_TEXTURE_2D, textures_[i]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, + GL_LUMINANCE, GL_UNSIGNED_BYTE, video_frame->data(i)); + } + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + context()->SwapBuffers(); +} diff --git a/media/tools/shader_bench/gpu_color_painter.h b/media/tools/shader_bench/gpu_color_painter.h new file mode 100644 index 0000000..0c053af --- /dev/null +++ b/media/tools/shader_bench/gpu_color_painter.h @@ -0,0 +1,34 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_H_ +#define MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_H_ + +#include "app/gfx/gl/gl_context.h" +#include "base/scoped_ptr.h" +#include "media/base/video_frame.h" +#include "media/tools/shader_bench/gpu_painter.h" + +// Does color space conversion using luminance textures on GPU, +// renders using GPU. +class GPUColorWithLuminancePainter : public GPUPainter { + public: + GPUColorWithLuminancePainter(); + virtual ~GPUColorWithLuminancePainter(); + + // Painter interface. + virtual void Initialize(int width, int height); + virtual void Paint(scoped_refptr<media::VideoFrame> video_frame); + + private: + // Shader program id. + GLuint program_id_; + + // IDs of 3 luminance textures. + GLuint textures_[3]; + + DISALLOW_COPY_AND_ASSIGN(GPUColorWithLuminancePainter); +}; + +#endif // MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_H_ diff --git a/media/tools/shader_bench/gpu_color_painter_exp.cc b/media/tools/shader_bench/gpu_color_painter_exp.cc new file mode 100644 index 0000000..cb89e09 --- /dev/null +++ b/media/tools/shader_bench/gpu_color_painter_exp.cc @@ -0,0 +1,130 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/tools/shader_bench/gpu_color_painter_exp.h" + +// Matrix used for the YUV to RGB conversion. +static const float kYUV2RGB[9] = { + 1.f, 0.f, 1.403f, + 1.f, -.344f, -.714f, + 1.f, 1.772f, 0.f, +}; + +static const float kYUV2RGB_TRANS[9] = { + 1.f, 1.f, 1.f, + 0.f, -.344f, 1.772f, + 1.403f, -.714f, 0.f, +}; + +const float attributeSelector[4] = { + 1.f, 10.f, 100.f, 1000.f, +}; + +// Pass-through vertex shader. +static const char kVertexShader[] = + "precision highp float;\n" + "precision highp int;\n" + "varying vec2 interp_tc;\n" + "\n" + "attribute vec4 in_pos;\n" + "attribute vec2 in_tc;\n" + "\n" + "void main() {\n" + " interp_tc = in_tc;\n" + " gl_Position = in_pos;\n" + "}\n"; + +// YUV to RGB pixel shader. Loads a pixel from each plane and pass through the +// matrix. +static const char kFragmentShader[] = + "precision mediump float;\n" + "precision mediump int;\n" + "varying vec2 interp_tc;\n" + "\n" + "uniform sampler2D y_tex;\n" + "uniform sampler2D u_tex;\n" + "uniform sampler2D v_tex;\n" + "uniform mat3 yuv2rgb;\n" + "uniform vec4 attrib_vector; \n" + "uniform int texture_width; \n" + "\n" + "void main() {\n" + " float x_pos = interp_tc.x * float(texture_width); \n" + " float y_index = floor(mod(x_pos, 4.0)); \n" + " float y_denom = pow(10.0, y_index); \n" + " vec4 attrib_y = mod(floor(attrib_vector / y_denom), 10.0); \n" + " float uv_index = floor(mod(floor(x_pos / 4.0), 4.0)); \n" + " float uv_denom = pow(10.0, uv_index); \n" + " vec4 attrib_uv = mod(floor(attrib_vector / uv_denom), 10.0); \n" + " vec4 texel_y = texture2D(y_tex, interp_tc); \n" + " vec4 texel_u = texture2D(u_tex, interp_tc); \n" + " vec4 texel_v = texture2D(v_tex, interp_tc); \n" + " float y = dot(attrib_y, texel_y); \n" + " float u = dot(attrib_uv, texel_u) - 0.5; \n" + " float v = dot(attrib_uv, texel_v) - 0.5; \n" + " vec3 rgb = yuv2rgb * vec3(y, u, v);\n" + " gl_FragColor = vec4(rgb, 1);\n" + "}\n"; + +GPUColorRGBALumHackPainter::GPUColorRGBALumHackPainter() + : program_id_(-1) { +} + +GPUColorRGBALumHackPainter::~GPUColorRGBALumHackPainter() { + if (program_id_) { + glDeleteProgram(program_id_); + glDeleteTextures(media::VideoFrame::kNumYUVPlanes, textures_); + } +} + +void GPUColorRGBALumHackPainter::Initialize(int width, int height) { + glGenTextures(3, textures_); + for (unsigned int i = 0; i < media::VideoFrame::kNumYUVPlanes; ++i) { + unsigned int texture_width = (i == media::VideoFrame::kYPlane) ? + width : width / 2; + unsigned int texture_height = (i == media::VideoFrame::kYPlane) ? + height : height / 2; + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, textures_[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width / 4, texture_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + + GLuint program = CreateShaderProgram(kVertexShader, kFragmentShader); + + // Bind parameters. + glUniform1i(glGetUniformLocation(program, "y_tex"), 0); + glUniform1i(glGetUniformLocation(program, "u_tex"), 1); + glUniform1i(glGetUniformLocation(program, "v_tex"), 2); + glUniform1i(glGetUniformLocation(program, "texture_width"), width); + int yuv2rgb_location = glGetUniformLocation(program, "yuv2rgb"); + if (gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) + glUniformMatrix3fv(yuv2rgb_location, 1, GL_TRUE, kYUV2RGB); + else + glUniformMatrix3fv(yuv2rgb_location, 1, GL_FALSE, kYUV2RGB_TRANS); + int attrib_location = glGetUniformLocation(program, "attrib_vector"); + glUniform4fv(attrib_location, 1, attributeSelector); + + program_id_ = program; +} + +void GPUColorRGBALumHackPainter::Paint( + scoped_refptr<media::VideoFrame> video_frame) { + for (unsigned int i = 0; i < media::VideoFrame::kNumYUVPlanes; ++i) { + unsigned int width = (i == media::VideoFrame::kYPlane) ? + video_frame->width() : video_frame->width() / 2; + unsigned int height = (i == media::VideoFrame::kYPlane) ? + video_frame->height() : video_frame->height() / 2; + glBindTexture(GL_TEXTURE_2D, textures_[i]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 4, height, + GL_RGBA, GL_UNSIGNED_BYTE, video_frame->data(i)); + } + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + context()->SwapBuffers(); +} diff --git a/media/tools/shader_bench/gpu_color_painter_exp.h b/media/tools/shader_bench/gpu_color_painter_exp.h new file mode 100644 index 0000000..c382f62 --- /dev/null +++ b/media/tools/shader_bench/gpu_color_painter_exp.h @@ -0,0 +1,35 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_EXP_H_ +#define MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_EXP_H_ + +#include "app/gfx/gl/gl_context.h" +#include "base/scoped_ptr.h" +#include "media/base/video_frame.h" +#include "media/tools/shader_bench/gpu_painter.h" + +// Does color space conversion using RGBA textures acting as +// luminance textures (experimental shader hack) for YUV->RGBA +// conversion; renders using GPU. +class GPUColorRGBALumHackPainter : public GPUPainter { + public: + GPUColorRGBALumHackPainter(); + virtual ~GPUColorRGBALumHackPainter(); + + // Painter interface. + virtual void Initialize(int width, int height); + virtual void Paint(scoped_refptr<media::VideoFrame> video_frame); + + private: + // Shader program id. + GLuint program_id_; + + // IDs of 3 RGBA textures, pretending to be luminance textures. + GLuint textures_[3]; + + DISALLOW_COPY_AND_ASSIGN(GPUColorRGBALumHackPainter); +}; + +#endif // MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_EXP_H_ diff --git a/media/tools/shader_bench/gpu_painter.cc b/media/tools/shader_bench/gpu_painter.cc new file mode 100644 index 0000000..c1fe5ae --- /dev/null +++ b/media/tools/shader_bench/gpu_painter.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "media/tools/shader_bench/gpu_painter.h" + +// Vertices for a full screen quad. +static const float kVertices[8] = { + -1.f, 1.f, + -1.f, -1.f, + 1.f, 1.f, + 1.f, -1.f, +}; + +// Texture Coordinates mapping the entire texture. +static const float kTextureCoords[8] = { + 0, 0, + 0, 1, + 1, 0, + 1, 1, +}; + +// Buffer size for compile errors. +static const unsigned int kErrorSize = 4096; + +GPUPainter::GPUPainter() + : context_(NULL) { +} + +GPUPainter::~GPUPainter() { +} + +void GPUPainter::SetGLContext(gfx::GLContext* context) { + context_ = context; +} + +GLuint GPUPainter::LoadShader(unsigned type, const char* shader_source) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &shader_source, NULL); + glCompileShader(shader); + int result = GL_FALSE; + glGetShaderiv(shader, GL_COMPILE_STATUS, &result); + if (!result) { + char log[kErrorSize]; + int len; + glGetShaderInfoLog(shader, kErrorSize - 1, &len, log); + log[kErrorSize - 1] = 0; + LOG(FATAL) << "Shader did not compile: " << log; + } + return shader; +} + +GLuint GPUPainter::CreateShaderProgram(const char* vertex_shader_source, + const char* fragment_shader_source) { + + // Create vertex and pixel shaders. + GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER, vertex_shader_source); + GLuint fragment_shader = + LoadShader(GL_FRAGMENT_SHADER, fragment_shader_source); + + // Create program and attach shaders. + GLuint program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + glLinkProgram(program); + int result = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &result); + if (!result) { + char log[kErrorSize]; + int len; + glGetProgramInfoLog(program, kErrorSize - 1, &len, log); + log[kErrorSize - 1] = 0; + LOG(FATAL) << "Program did not link: " << log; + } + glUseProgram(program); + + // Set common vertex parameters. + int pos_location = glGetAttribLocation(program, "in_pos"); + glEnableVertexAttribArray(pos_location); + glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); + + int tc_location = glGetAttribLocation(program, "in_tc"); + glEnableVertexAttribArray(tc_location); + glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords); + return program; +} diff --git a/media/tools/shader_bench/gpu_painter.h b/media/tools/shader_bench/gpu_painter.h new file mode 100644 index 0000000..a8d4caf --- /dev/null +++ b/media/tools/shader_bench/gpu_painter.h @@ -0,0 +1,42 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_TOOLS_SHADER_BENCH_GPU_PAINTER_H_ +#define MEDIA_TOOLS_SHADER_BENCH_GPU_PAINTER_H_ + +#include "app/gfx/gl/gl_bindings.h" +#include "app/gfx/gl/gl_context.h" +#include "app/gfx/gl/gl_implementation.h" +#include "media/tools/shader_bench/painter.h" + +// Class that renders video frames to a window via GPU. +class GPUPainter : public Painter { + public: + GPUPainter(); + virtual ~GPUPainter(); + + // Returns a reference to the GL context. + gfx::GLContext* context() const { return context_; } + + // Sets context for subsequent gl calls in this painter. + virtual void SetGLContext(gfx::GLContext* context); + + // Creates shader program into given context, from the vertex and fragment + // shader source code. Returns the id of the shader program. + virtual GLuint CreateShaderProgram(const char* vertex_shader_source, + const char* fragment_shader_source); + + private: + // Loads shader into given context, from the source code of the + // shader. type refers to the shader type, either GL_VERTEX_SHADER or + // GL_FRAGMENT_SHADER. Returns id of shader. + GLuint LoadShader(unsigned type, const char* shader_source); + + // Reference to the gl context. + gfx::GLContext* context_; + + DISALLOW_COPY_AND_ASSIGN(GPUPainter); +}; + +#endif // MEDIA_TOOLS_SHADER_BENCH_GPU_PAINTER_H_ diff --git a/media/tools/shader_bench/painter.cc b/media/tools/shader_bench/painter.cc new file mode 100644 index 0000000..ab8fc59 --- /dev/null +++ b/media/tools/shader_bench/painter.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/tools/shader_bench/painter.h" + +Painter::Painter() + : frames_(NULL) { +} + +Painter::~Painter() { +} + +void Painter::OnPaint() { + if (frames_ && !frames_->empty()) { + scoped_refptr<media::VideoFrame> cur_frame = frames_->front(); + Paint(cur_frame); + frames_->pop_front(); + frames_->push_back(cur_frame); + } +} + +void Painter::LoadFrames( + std::deque<scoped_refptr<media::VideoFrame> >* frames) { + frames_ = frames; +} diff --git a/media/tools/shader_bench/painter.h b/media/tools/shader_bench/painter.h new file mode 100644 index 0000000..6b4ba09 --- /dev/null +++ b/media/tools/shader_bench/painter.h @@ -0,0 +1,39 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_TOOLS_SHADER_BENCH_PAINTER_H_ +#define MEDIA_TOOLS_SHADER_BENCH_PAINTER_H_ + +#include <deque> + +#include "base/scoped_ptr.h" +#include "media/base/video_frame.h" + +// Class that paints video frames to a window. +class Painter { + public: + Painter(); + virtual ~Painter(); + + // Loads frames into Painter. Painter does not take ownership of frames. + virtual void LoadFrames( + std::deque<scoped_refptr<media::VideoFrame> >* frames); + + // Called window is ready to be painted. + virtual void OnPaint(); + + // Initialize a Painter class with a width and a height + virtual void Initialize(int width, int height) = 0; + + // Paint a single frame to a window. + virtual void Paint(scoped_refptr<media::VideoFrame> video_frame) = 0; + + private: + // Frames that the Painter will paint. + std::deque<scoped_refptr<media::VideoFrame> >* frames_; + + DISALLOW_COPY_AND_ASSIGN(Painter); +}; + +#endif // MEDIA_TOOLS_SHADER_BENCH_PAINTER_H_ diff --git a/media/tools/shader_bench/shader_bench.cc b/media/tools/shader_bench/shader_bench.cc new file mode 100644 index 0000000..6bdfe67 --- /dev/null +++ b/media/tools/shader_bench/shader_bench.cc @@ -0,0 +1,163 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <deque> +#include <iostream> +#include <stdio.h> +#include <stdlib.h> + +#include "app/gfx/gl/gl_bindings.h" +#include "app/gfx/gl/gl_context.h" +#include "app/gfx/gl/gl_implementation.h" +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/time.h" +#include "gfx/native_widget_types.h" +#include "media/base/callback.h" +#include "media/base/video_frame.h" +#include "media/tools/shader_bench/cpu_color_painter.h" +#include "media/tools/shader_bench/gpu_color_painter.h" +#include "media/tools/shader_bench/gpu_color_painter_exp.h" +#include "media/tools/shader_bench/painter.h" +#include "media/tools/shader_bench/window.h" + +#if defined(OS_LINUX) +#include <gtk/gtk.h> +#endif + +static const int kNumFramesToPaint = 500; +static base::TimeTicks g_start_; +static base::TimeTicks g_end_; + +long CalculateYUVFrameSize(FILE* file_handle, int num_frames) { + fseek(file_handle, 0, SEEK_END); + long file_size = (long) ftell(file_handle); + rewind(file_handle); + return file_size / num_frames; +} + +void GetFrames(std::string file_name, + int width, int height, int num_frames, + std::deque<scoped_refptr<media::VideoFrame> >& out_frames) { + FILE* file_handle = fopen(file_name.c_str(), "rb"); + if (!file_handle) { + printf("Could not open %s\n", file_name.c_str()); + exit(1); + } + + long frame_size = CalculateYUVFrameSize(file_handle, num_frames); + + for (int i = 0; i < num_frames; i++) { + scoped_refptr<media::VideoFrame> video_frame; + + media::VideoFrame::CreateFrame(media::VideoFrame::YV12, + width, + height, + base::TimeDelta(), + base::TimeDelta(), + &video_frame); + long bytes_read = + fread(video_frame->data(0), 1, frame_size, file_handle); + + if (bytes_read != frame_size) { + printf("Could not read %s\n", file_name.c_str()); + fclose(file_handle); + exit(1); + } + out_frames.push_back(video_frame); + } + + fclose(file_handle); +} + +void TestFinished() { + g_end_ = base::TimeTicks::HighResNow(); + double time_in_seconds = + static_cast<double>((g_end_ - g_start_).InMilliseconds()) / 1000; + double fps = kNumFramesToPaint / time_in_seconds; + printf("Printed %f frames per second.\n", fps); +} + +void RunTest(media::Window* window, Painter* painter) { + g_start_ = base::TimeTicks::HighResNow(); + window->Start(kNumFramesToPaint, NewRunnableFunction(&TestFinished), painter); +} + +int main(int argc, char** argv) { + // Read arguments. + if (argc == 1) { + printf("Usage: %s --file=FILE --wxh=DIMENSIONS --frames=NUM_FRAMES\n" + "FILE is a raw .yuv file with 1+ frames in it\n" + "DIMENSIONS is the width and height of the frame in pixels\n" + "NUM_FRAMES is the number of frames in FILE\n", argv[0]); + return 1; + } + + // Read command line. +#if defined(OS_LINUX) + gtk_init(&argc, &argv); +#endif + CommandLine::Init(argc, argv); + + // Determine file name. + std::string file_name = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII("file"); + + // Determine number of frames. + int num_frames = 0; + std::string str_num_frames = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII("frames"); + base::StringToInt(str_num_frames, &num_frames); + + // Determine video dimensions. + int width = 0; + int height = 0; + std::string dimensions = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII("wxh"); + int x_index = dimensions.find('x'); + std::string str_width = dimensions.substr(0, x_index); + std::string str_height = + dimensions.substr(x_index + 1, dimensions.length() - x_index - 1); + base::StringToInt(str_width, &width); + base::StringToInt(str_height, &height); + + // Process files. + std::deque<scoped_refptr<media::VideoFrame> > frames; + GetFrames(file_name, width, height, num_frames, frames); + + // Initialize window and graphics context. + base::AtExitManager at_exit_manager; + gfx::GLContext::InitializeOneOff(); + scoped_ptr<media::Window> window(new media::Window(width, height)); + gfx::GLContext* context = + gfx::GLContext::CreateViewGLContext(window->PluginWindow(), false); + context->MakeCurrent(); + // This sets D3DPRESENT_INTERVAL_IMMEDIATE on Windows. + context->SetSwapInterval(0); + + // Initialize and name GPU painters. + static const int kNumPainters = 3; + static const struct { + const char* name; + GPUPainter* painter; + } painters[] = { + { "CPU CSC + GPU Render", new CPUColorPainter() }, + { "GPU CSC/Render", new GPUColorWithLuminancePainter() }, + { "GPU CSC/Render (experimental)", new GPUColorRGBALumHackPainter() }, + }; + + // Run GPU painter tests. + for (int i = 0; i < kNumPainters; i++) { + scoped_ptr<GPUPainter> painter(painters[i].painter); + painter->LoadFrames(&frames); + painter->SetGLContext(context); + painter->Initialize(width, height); + printf("Running %s tests...", painters[i].name); + RunTest(window.get(), painter.get()); + } + + return 0; +} diff --git a/media/tools/shader_bench/window.cc b/media/tools/shader_bench/window.cc new file mode 100644 index 0000000..b14a4c7 --- /dev/null +++ b/media/tools/shader_bench/window.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/tools/shader_bench/window.h" + +#include "base/task.h" + +namespace media { + +Window::Window(int width, int height) + : done_task_(NULL), + painter_(NULL), + limit_(0), + count_(0), + running_(false) { + window_handle_ = CreateNativeWindow(width, height); +} + +} // namespace media diff --git a/media/tools/shader_bench/window.h b/media/tools/shader_bench/window.h new file mode 100644 index 0000000..b17786a --- /dev/null +++ b/media/tools/shader_bench/window.h @@ -0,0 +1,60 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_TOOLS_SHADER_BENCH_WINDOW_H_ +#define MEDIA_TOOLS_SHADER_BENCH_WINDOW_H_ + +#include "gfx/native_widget_types.h" + +class Painter; +class Task; + +namespace media { + +class Window { + public: + Window(int width, int height); + + // Creates and returns a handle to a native window of the given dimensions. + gfx::NativeWindow CreateNativeWindow(int width, int height); + + // Returns the NPAPI plugin window handle of the window. + gfx::PluginWindowHandle PluginWindow(); + + // Kicks off frame painting with the given limit, painter, and + // callback to run when painting task is complete. + void Start(int limit, Task* done_task, Painter* painter); + + // Called when window is expected to paint self. + void OnPaint(); + + // Main loop for window. + void MainLoop(); + + private: + // Task to run when frame painting is completed. Will be deleted after + // running. + Task* done_task_; + + // Reference to painter Window uses to paint frames. + Painter* painter_; + + // Number of frames to paint before closing the window. + int limit_; + + // Number of frames currently painted. + int count_; + + // True if the window is painting video frames to the screen, false otherwise. + bool running_; + + // This window's native handle. + gfx::NativeWindow window_handle_; + + DISALLOW_COPY_AND_ASSIGN(Window); +}; + +} // namespace media + +#endif // MEDIA_TOOLS_SHADER_BENCH_WINDOW_H_ diff --git a/media/tools/shader_bench/window_linux.cc b/media/tools/shader_bench/window_linux.cc new file mode 100644 index 0000000..1c43505 --- /dev/null +++ b/media/tools/shader_bench/window_linux.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/tools/shader_bench/window.h" + +#include "base/task.h" +#include "media/tools/shader_bench/painter.h" + +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +namespace { + +gboolean OnDelete(GtkWidget* widget, GdkEventExpose* event) { + gtk_main_quit(); + return FALSE; +} + +gboolean OnExpose(GtkWidget* widget, GdkEventExpose* event, gpointer data) { + media::Window* window = reinterpret_cast<media::Window*>(data); + if (window) + window->OnPaint(); + return FALSE; +} + +} // namespace + +namespace media { + +gfx::NativeWindow Window::CreateNativeWindow(int width, int height) { + GtkWidget* hwnd = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_default_size(GTK_WINDOW(hwnd), width, height); + gtk_widget_set_double_buffered(hwnd, FALSE); + gtk_widget_set_app_paintable(hwnd, TRUE); + gtk_widget_show(hwnd); + + return GTK_WINDOW(hwnd); +} + +gfx::PluginWindowHandle Window::PluginWindow() { + return GDK_WINDOW_XWINDOW(GTK_WIDGET(window_handle_)->window); +} + +void Window::Start(int limit, Task* done_task, Painter* painter) { + running_ = true; + count_ = 0; + limit_ = limit; + done_task_ = done_task; + painter_ = painter; + + gtk_signal_connect(GTK_OBJECT(window_handle_), + "delete_event", + reinterpret_cast<GtkSignalFunc>(::OnDelete), + NULL); + + gtk_signal_connect(GTK_OBJECT(window_handle_), + "expose_event", + reinterpret_cast<GtkSignalFunc>(::OnExpose), + this); + + gtk_widget_queue_draw(GTK_WIDGET(window_handle_)); + MainLoop(); +} + +void Window::OnPaint() { + if (!running_) + return; + + if (count_ < limit_) { + painter_->OnPaint(); + count_++; + gtk_widget_queue_draw(GTK_WIDGET(window_handle_)); + } else { + running_ = false; + if (done_task_) { + done_task_->Run(); + delete done_task_; + } + gtk_main_quit(); + } +} + +void Window::MainLoop() { + gtk_main(); +} + +} // namespace media diff --git a/media/tools/shader_bench/window_win.cc b/media/tools/shader_bench/window_win.cc new file mode 100644 index 0000000..0d9c3f9 --- /dev/null +++ b/media/tools/shader_bench/window_win.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/tools/shader_bench/window.h" + +#include "base/task.h" +#include "media/tools/shader_bench/painter.h" + +namespace { + +LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, + WPARAM w_param, LPARAM l_param) { + LRESULT result = 0; + switch (msg) { + case WM_CLOSE: + ::DestroyWindow(hwnd); + break; + case WM_DESTROY: + ::PostQuitMessage(0); + break; + case WM_ERASEBKGND: + // Return a non-zero value to indicate that the background has been + // erased. + result = 1; + break; + case WM_PAINT: { + media::Window* window = + reinterpret_cast<media::Window*>( + GetWindowLongPtr(hwnd, GWL_USERDATA)); + if (window != NULL) + window->OnPaint(); + ::ValidateRect(hwnd, NULL); + break; + } + default: + result = ::DefWindowProc(hwnd, msg, w_param, l_param); + break; + } + return result; +} + +} // namespace + +namespace media { + +gfx::NativeWindow Window::CreateNativeWindow(int width, int height) { + WNDCLASS wnd_class = {0}; + HINSTANCE instance = GetModuleHandle(NULL); + wnd_class.style = CS_OWNDC; + wnd_class.lpfnWndProc = WindowProc; + wnd_class.hInstance = instance; + wnd_class.hbrBackground = + reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)); + wnd_class.lpszClassName = L"gpu_demo"; + if (!RegisterClass(&wnd_class)) + return NULL; + + DWORD wnd_style = WS_OVERLAPPED | WS_SYSMENU; + RECT wnd_rect; + wnd_rect.left = 0; + wnd_rect.top = 0; + wnd_rect.right = width; + wnd_rect.bottom = height; + AdjustWindowRect(&wnd_rect, wnd_style, FALSE); + + HWND hwnd = CreateWindow( + wnd_class.lpszClassName, + L"", + wnd_style, + 0, + 0, + wnd_rect.right - wnd_rect.left, + wnd_rect.bottom - wnd_rect.top, + NULL, + NULL, + instance, + NULL); + if (hwnd == NULL) + return NULL; + + return hwnd; +} + +gfx::PluginWindowHandle Window::PluginWindow() { + return window_handle_; +} + +void Window::Start(int limit, Task* done_task, Painter* painter) { + running_ = true; + count_ = 0; + limit_ = limit; + done_task_ = done_task; + painter_ = painter; + + SetWindowLongPtr(window_handle_, GWL_USERDATA, + reinterpret_cast<LONG_PTR>(this)); + + ShowWindow(window_handle_, SW_SHOWNORMAL); + + // Post first invalidate call to kick off painting. + ::InvalidateRect(window_handle_, NULL, FALSE); + + MainLoop(); +} + +void Window::OnPaint() { + if (!running_) + return; + + if (count_ < limit_) { + painter_->OnPaint(); + count_++; + } else { + running_ = false; + if (done_task_) { + ShowWindow(window_handle_, SW_HIDE); + done_task_->Run(); + delete done_task_; + } + } +} + +void Window::MainLoop() { + MSG msg; + bool done = false; + while (!done) { + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT || !running_) + done = true; + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + if (!done) + ::InvalidateRect(window_handle_, NULL, FALSE); + } + } +} + +} // namespace media |