diff options
author | nick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-08 01:21:53 +0000 |
---|---|---|
committer | nick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-08 01:21:53 +0000 |
commit | 5f3daf544cb1f74fae21693dafba8d108dd4414a (patch) | |
tree | c848c1882529bb9e723b55cd7b6584194ecc7659 /ui/surface | |
parent | 2a23d3f51204fa9feeafe8c8b03119c56f4aea44 (diff) | |
download | chromium_src-5f3daf544cb1f74fae21693dafba8d108dd4414a.zip chromium_src-5f3daf544cb1f74fae21693dafba8d108dd4414a.tar.gz chromium_src-5f3daf544cb1f74fae21693dafba8d108dd4414a.tar.bz2 |
Two techniques are introduced implementing RGB->YV12 conversion using fragment shaders. This functionality is added to AcceleratedSurfaceTransformer, and validated by new tests checking accuracy against a reference implementation.
The faster ("MRT") technique needs hardware support for multiple simultaneous render targets, and takes two passes, the second pass reading one-quarter as much data as the first pass. The slower "WithoutMRT" technique requires three passes and reads the full RGB input with each pass.
On two machines I tested (one NVIDIA, one Intel), the fast path was about twice the speed of the dumb path. On an NVIDIA Quadro 6000 using the fast path, at 1280x720 frame size, conversion ran at 2838 fps. Accuracy was within two bits of the software implementation, and usually within one bit.
BUG=161537
TEST=Included unit tests.
Review URL: https://codereview.chromium.org/11280318
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175446 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/surface')
-rw-r--r-- | ui/surface/DEPS | 3 | ||||
-rw-r--r-- | ui/surface/accelerated_surface_transformer_win.cc | 857 | ||||
-rw-r--r-- | ui/surface/accelerated_surface_transformer_win.h | 265 | ||||
-rw-r--r-- | ui/surface/accelerated_surface_transformer_win.hlsl | 272 | ||||
-rw-r--r-- | ui/surface/accelerated_surface_transformer_win_unittest.cc | 421 | ||||
-rw-r--r-- | ui/surface/d3d9_utils_win.cc | 254 | ||||
-rw-r--r-- | ui/surface/d3d9_utils_win.h | 146 | ||||
-rw-r--r-- | ui/surface/surface.gyp | 2 |
8 files changed, 1661 insertions, 559 deletions
diff --git a/ui/surface/DEPS b/ui/surface/DEPS new file mode 100644 index 0000000..cc5cd70 --- /dev/null +++ b/ui/surface/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+media/base", +] diff --git a/ui/surface/accelerated_surface_transformer_win.cc b/ui/surface/accelerated_surface_transformer_win.cc index cfc031b..694f169 100644 --- a/ui/surface/accelerated_surface_transformer_win.cc +++ b/ui/surface/accelerated_surface_transformer_win.cc @@ -1,261 +1,596 @@ -// Copyright (c) 2012 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 "ui/surface/accelerated_surface_transformer_win.h"
-
-#include <vector>
-
-#include "accelerated_surface_transformer_win_hlsl_compiled.h"
-#include "base/debug/trace_event.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/win/scoped_comptr.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
-#include "ui/surface/d3d9_utils_win.h"
-#include "ui/surface/surface_export.h"
-
-using base::win::ScopedComPtr;
-using std::vector;
-
-namespace d3d_utils = ui_surface_d3d9_utils;
-
-namespace {
-
-struct Vertex {
- float x, y, z, w;
- float u, v;
-};
-
-const static D3DVERTEXELEMENT9 g_vertexElements[] = {
- { 0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0 },
- { 0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0 },
- D3DDECL_END()
-};
-
-// Calculate the number necessary to transform |src_subrect| into |dst_size|
-// by repeating downsampling of the image of |src_subrect| by a factor no more
-// than 2.
-int GetResampleCount(const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- const gfx::Size& back_buffer_size) {
- // At least one copy is required, since the back buffer itself is not
- // lockable.
- int min_resample_count = 1;
- int width_count = 0;
- int width = src_subrect.width();
- while (width > dst_size.width()) {
- ++width_count;
- width >>= 1;
- }
- int height_count = 0;
- int height = src_subrect.height();
- while (height > dst_size.height()) {
- ++height_count;
- height >>= 1;
- }
- return std::max(std::max(width_count, height_count),
- min_resample_count);
-}
-
-// Returns half the size of |size| no smaller than |min_size|.
-gfx::Size GetHalfSizeNoLessThan(const gfx::Size& size,
- const gfx::Size& min_size) {
- return gfx::Size(std::max(min_size.width(), size.width() / 2),
- std::max(min_size.height(), size.height() / 2));
-}
-
-gfx::Size GetSize(IDirect3DSurface9* surface) {
- D3DSURFACE_DESC surface_description;
- HRESULT hr = surface->GetDesc(&surface_description);
- if (FAILED(hr))
- return gfx::Size(0, 0);
- return gfx::Size(surface_description.Width, surface_description.Height);
-}
-
-} // namespace
-
-
-AcceleratedSurfaceTransformer::AcceleratedSurfaceTransformer() {}
-
-bool AcceleratedSurfaceTransformer::Init(IDirect3DDevice9* device) {
- device_ = device;
- if (!InitShaderCombo(
- ui_surface::AcceleratedSurfaceTransformerWinHLSL::kVsOneTexture,
- ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsOneTexture,
- SIMPLE_TEXTURE)) {
- ReleaseAll();
- return false;
- }
-
- base::win::ScopedComPtr<IDirect3DVertexDeclaration9> vertex_declaration;
- HRESULT hr = device_->CreateVertexDeclaration(g_vertexElements,
- vertex_declaration.Receive());
- if (!SUCCEEDED(hr)) {
- ReleaseAll();
- return false;
- }
- device_->SetVertexDeclaration(vertex_declaration);
-
- return true;
-}
-
-bool AcceleratedSurfaceTransformer::InitShaderCombo(
- const BYTE vertex_shader_instructions[],
- const BYTE pixel_shader_instructions[],
- ShaderCombo shader_combo_name) {
- HRESULT hr = device_->CreateVertexShader(
- reinterpret_cast<const DWORD*>(vertex_shader_instructions),
- vertex_shaders_[shader_combo_name].Receive());
-
- if (FAILED(hr))
- return false;
-
- hr = device_->CreatePixelShader(
- reinterpret_cast<const DWORD*>(pixel_shader_instructions),
- pixel_shaders_[shader_combo_name].Receive());
-
- return SUCCEEDED(hr);
-}
-
-
-void AcceleratedSurfaceTransformer::ReleaseAll() {
- for (int i = 0; i < NUM_SHADERS; i++) {
- vertex_shaders_[i] = NULL;
- vertex_shaders_[i] = NULL;
- }
- device_ = NULL;
-}
-void AcceleratedSurfaceTransformer::DetachAll() {
- for (int i = 0; i < NUM_SHADERS; i++) {
- vertex_shaders_[i].Detach();
- vertex_shaders_[i].Detach();
- }
- device_.Detach();
-}
-
-// Draw a textured quad to a surface.
-bool AcceleratedSurfaceTransformer::CopyInverted(
- IDirect3DTexture9* src_texture,
- IDirect3DSurface9* dst_surface,
- const gfx::Size& dst_size) {
- base::win::ScopedComPtr<IDirect3DSurface9> default_color_target;
- device()->GetRenderTarget(0, default_color_target.Receive());
-
- if (!SetShaderCombo(SIMPLE_TEXTURE))
- return false;
-
- device()->SetRenderTarget(0, dst_surface);
- device()->SetTexture(0, src_texture);
-
- D3DVIEWPORT9 viewport = {
- 0, 0,
- dst_size.width(), dst_size.height(),
- 0, 1
- };
- device()->SetViewport(&viewport);
-
- float halfPixelX = -1.0f / dst_size.width();
- float halfPixelY = 1.0f / dst_size.height();
- Vertex vertices[] = {
- { halfPixelX - 1, halfPixelY + 1, 0.5f, 1, 0, 1 },
- { halfPixelX + 1, halfPixelY + 1, 0.5f, 1, 1, 1 },
- { halfPixelX + 1, halfPixelY - 1, 0.5f, 1, 1, 0 },
- { halfPixelX - 1, halfPixelY - 1, 0.5f, 1, 0, 0 }
- };
-
- device()->BeginScene();
- device()->DrawPrimitiveUP(D3DPT_TRIANGLEFAN,
- 2,
- vertices,
- sizeof(vertices[0]));
- device()->EndScene();
-
- // Clear surface references.
- device()->SetRenderTarget(0, default_color_target);
- device()->SetTexture(0, NULL);
- return true;
-}
-
-// Resize an RGB surface using repeated linear interpolation.
-bool AcceleratedSurfaceTransformer::ResizeBilinear(
- IDirect3DSurface9* src_surface,
- const gfx::Rect& src_subrect,
- IDirect3DSurface9* dst_surface) {
- gfx::Size src_size = GetSize(src_surface);
- gfx::Size dst_size = GetSize(dst_surface);
-
- if (src_size.IsEmpty() || dst_size.IsEmpty())
- return false;
-
- HRESULT hr = S_OK;
- // Set up intermediate buffers needed for downsampling.
- const int resample_count =
- GetResampleCount(src_subrect, dst_size, src_size);
- base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2];
- const gfx::Size half_size =
- GetHalfSizeNoLessThan(src_subrect.size(), dst_size);
- if (resample_count > 1) {
- TRACE_EVENT0("gpu", "CreateTemporarySurface");
- if (!d3d_utils::CreateTemporaryLockableSurface(device(),
- half_size,
- temp_buffer[0].Receive()))
- return false;
- }
- if (resample_count > 2) {
- TRACE_EVENT0("gpu", "CreateTemporarySurface");
- const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, dst_size);
- if (!d3d_utils::CreateTemporaryLockableSurface(device(),
- quarter_size,
- temp_buffer[1].Receive()))
- return false;
- }
-
- // Repeat downsampling the surface until its size becomes identical to
- // |dst_size|. We keep the factor of each downsampling no more than two
- // because using a factor more than two can introduce aliasing.
- RECT read_rect = src_subrect.ToRECT();
- gfx::Size write_size = half_size;
- int read_buffer_index = 1;
- int write_buffer_index = 0;
- for (int i = 0; i < resample_count; ++i) {
- TRACE_EVENT0("gpu", "StretchRect");
- IDirect3DSurface9* read_buffer =
- (i == 0) ? src_surface : temp_buffer[read_buffer_index];
- IDirect3DSurface9* write_buffer =
- (i == resample_count - 1) ? dst_surface :
- temp_buffer[write_buffer_index];
- RECT write_rect = gfx::Rect(write_size).ToRECT();
- hr = device()->StretchRect(read_buffer,
- &read_rect,
- write_buffer,
- &write_rect,
- D3DTEXF_LINEAR);
-
- if (FAILED(hr))
- return false;
- read_rect = write_rect;
- write_size = GetHalfSizeNoLessThan(write_size, dst_size);
- std::swap(read_buffer_index, write_buffer_index);
- }
-
- return true;
-}
-
-IDirect3DDevice9* AcceleratedSurfaceTransformer::device() {
- return device_;
-}
-
-bool AcceleratedSurfaceTransformer::SetShaderCombo(ShaderCombo combo) {
- HRESULT hr = device()->SetVertexShader(vertex_shaders_[combo]);
- if (!SUCCEEDED(hr))
- return false;
- hr = device()->SetPixelShader(pixel_shaders_[combo]);
- if (!SUCCEEDED(hr))
- return false;
- return true;
-}
\ No newline at end of file +// Copyright (c) 2012 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 "ui/surface/accelerated_surface_transformer_win.h" + +#include <vector> + +#include "accelerated_surface_transformer_win_hlsl_compiled.h" +#include "base/debug/trace_event.h" +#include "base/memory/ref_counted.h" +#include "base/metrics/histogram.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/win/scoped_comptr.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" +#include "ui/surface/d3d9_utils_win.h" +#include "ui/surface/surface_export.h" + +using base::win::ScopedComPtr; +using std::vector; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsConvertRGBtoY8UV44; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsConvertUV44toU2V2; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsOneTexture; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kVsFetch2Pixels; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kVsFetch4Pixels; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kVsOneTexture; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kVsFetch4PixelsScale2; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsConvertRGBtoY; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsConvertRGBtoU; +using ui_surface::AcceleratedSurfaceTransformerWinHLSL::kPsConvertRGBtoV; + +namespace d3d_utils = ui_surface_d3d9_utils; + +namespace { + +struct Vertex { + float x, y, z, w; + float u, v; +}; + +const static D3DVERTEXELEMENT9 g_vertexElements[] = { + { 0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0 }, + { 0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0 }, + D3DDECL_END() +}; + +class ScopedRenderTargetRestorer { + public: + ScopedRenderTargetRestorer(IDirect3DDevice9* device, + int render_target_id) + : device_(device), + target_id_(render_target_id) { + device_->GetRenderTarget(target_id_, original_render_target_.Receive()); + } + ~ScopedRenderTargetRestorer() { + device_->SetRenderTarget(target_id_, original_render_target_); + } + private: + ScopedComPtr<IDirect3DDevice9> device_; + int target_id_; + ScopedComPtr<IDirect3DSurface9> original_render_target_; +}; + +// Calculate the number necessary to transform |src_subrect| into |dst_size| +// by repeating downsampling of the image of |src_subrect| by a factor no more +// than 2. +int GetResampleCount(const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const gfx::Size& back_buffer_size) { + // At least one copy is required, since the back buffer itself is not + // lockable. + int min_resample_count = 1; + int width_count = 0; + int width = src_subrect.width(); + while (width > dst_size.width()) { + ++width_count; + width >>= 1; + } + int height_count = 0; + int height = src_subrect.height(); + while (height > dst_size.height()) { + ++height_count; + height >>= 1; + } + return std::max(std::max(width_count, height_count), + min_resample_count); +} + +// Returns half the size of |size| no smaller than |min_size|. +gfx::Size GetHalfSizeNoLessThan(const gfx::Size& size, + const gfx::Size& min_size) { + return gfx::Size(std::max(min_size.width(), size.width() / 2), + std::max(min_size.height(), size.height() / 2)); +} + +} // namespace + +AcceleratedSurfaceTransformer::AcceleratedSurfaceTransformer() + : device_supports_multiple_render_targets_(false), + vertex_shader_sources_(), + pixel_shader_sources_() { + + // Associate passes with actual shader programs. + vertex_shader_sources_[ONE_TEXTURE] = kVsOneTexture; + pixel_shader_sources_[ONE_TEXTURE] = kPsOneTexture; + + vertex_shader_sources_[RGB_TO_YV12_FAST__PASS_1_OF_2] = kVsFetch4Pixels; + pixel_shader_sources_[RGB_TO_YV12_FAST__PASS_1_OF_2] = kPsConvertRGBtoY8UV44; + + vertex_shader_sources_[RGB_TO_YV12_FAST__PASS_2_OF_2] = kVsFetch2Pixels; + pixel_shader_sources_[RGB_TO_YV12_FAST__PASS_2_OF_2] = kPsConvertUV44toU2V2; + + vertex_shader_sources_[RGB_TO_YV12_SLOW__PASS_1_OF_3] = kVsFetch4Pixels; + pixel_shader_sources_[RGB_TO_YV12_SLOW__PASS_1_OF_3] = kPsConvertRGBtoY; + + vertex_shader_sources_[RGB_TO_YV12_SLOW__PASS_2_OF_3] = kVsFetch4PixelsScale2; + pixel_shader_sources_[RGB_TO_YV12_SLOW__PASS_2_OF_3] = kPsConvertRGBtoU; + + vertex_shader_sources_[RGB_TO_YV12_SLOW__PASS_3_OF_3] = kVsFetch4PixelsScale2; + pixel_shader_sources_[RGB_TO_YV12_SLOW__PASS_3_OF_3] = kPsConvertRGBtoV; + + COMPILE_ASSERT(NUM_SHADERS == 6, must_initialize_shader_sources); +} + +bool AcceleratedSurfaceTransformer::Init(IDirect3DDevice9* device) { + bool result = DoInit(device); + if (!result) { + ReleaseAll(); + } + return result; +} + +bool AcceleratedSurfaceTransformer::DoInit(IDirect3DDevice9* device) { + device_ = device; + + { + D3DCAPS9 caps; + HRESULT hr = device->GetDeviceCaps(&caps); + if (FAILED(hr)) + return false; + + device_supports_multiple_render_targets_ = (caps.NumSimultaneousRTs >= 2); + + // Log statistics about which paths we take. + UMA_HISTOGRAM_BOOLEAN("GPU.AcceleratedSurfaceTransformerCanUseMRT", + device_supports_multiple_render_targets()); + } + + // Force compilation of all shaders that could be used on this GPU. + if (!CompileShaderCombo(ONE_TEXTURE)) + return false; + + if (device_supports_multiple_render_targets()) { + if (!CompileShaderCombo(RGB_TO_YV12_FAST__PASS_1_OF_2) || + !CompileShaderCombo(RGB_TO_YV12_FAST__PASS_2_OF_2)) { + return false; + } + } else { + if (!CompileShaderCombo(RGB_TO_YV12_SLOW__PASS_1_OF_3) || + !CompileShaderCombo(RGB_TO_YV12_SLOW__PASS_2_OF_3) || + !CompileShaderCombo(RGB_TO_YV12_SLOW__PASS_3_OF_3)) { + return false; + } + } + COMPILE_ASSERT(NUM_SHADERS == 6, must_compile_at_doinit); + + base::win::ScopedComPtr<IDirect3DVertexDeclaration9> vertex_declaration; + HRESULT hr = device_->CreateVertexDeclaration(g_vertexElements, + vertex_declaration.Receive()); + if (FAILED(hr)) + return false; + hr = device_->SetVertexDeclaration(vertex_declaration); + if (FAILED(hr)) + return false; + + return true; +} + +bool AcceleratedSurfaceTransformer::CompileShaderCombo( + ShaderCombo shader) { + if (!vertex_shaders_[shader]) { + HRESULT hr = device_->CreateVertexShader( + reinterpret_cast<const DWORD*>(vertex_shader_sources_[shader]), + vertex_shaders_[shader].Receive()); + + if (FAILED(hr)) + return false; + + for (int i = 0; i < NUM_SHADERS; ++i) { + if (vertex_shader_sources_[i] == vertex_shader_sources_[shader] && + i != shader) { + vertex_shaders_[i] = vertex_shaders_[shader]; + } + } + } + + if (!pixel_shaders_[shader]) { + HRESULT hr = device_->CreatePixelShader( + reinterpret_cast<const DWORD*>(pixel_shader_sources_[shader]), + pixel_shaders_[shader].Receive()); + + if (FAILED(hr)) + return false; + + for (int i = 0; i < NUM_SHADERS; ++i) { + if (pixel_shader_sources_[i] == pixel_shader_sources_[shader] && + i != shader) { + pixel_shaders_[i] = pixel_shaders_[shader]; + } + } + } + + return true; +} + +void AcceleratedSurfaceTransformer::ReleaseAll() { + for (int i = 0; i < NUM_SHADERS; i++) { + vertex_shaders_[i] = NULL; + vertex_shaders_[i] = NULL; + } + device_ = NULL; +} +void AcceleratedSurfaceTransformer::DetachAll() { + for (int i = 0; i < NUM_SHADERS; i++) { + vertex_shaders_[i].Detach(); + vertex_shaders_[i].Detach(); + } + device_.Detach(); +} + +bool AcceleratedSurfaceTransformer::CopyInverted( + IDirect3DTexture9* src_texture, + IDirect3DSurface9* dst_surface, + const gfx::Size& dst_size) { + return CopyWithTextureScale(src_texture, dst_surface, dst_size, 1.0f, -1.0f); +} + +bool AcceleratedSurfaceTransformer::Copy( + IDirect3DTexture9* src_texture, + IDirect3DSurface9* dst_surface, + const gfx::Size& dst_size) { + return CopyWithTextureScale(src_texture, dst_surface, dst_size, 1.0f, 1.0f); +} + +bool AcceleratedSurfaceTransformer::CopyWithTextureScale( + IDirect3DTexture9* src_texture, + IDirect3DSurface9* dst_surface, + const gfx::Size& dst_size, + float texture_scale_x, + float texture_scale_y) { + + if (!SetShaderCombo(ONE_TEXTURE)) + return false; + + // Set the kTextureScale vertex shader constant, which is assigned to + // register 1. + float texture_scale[4] = {texture_scale_x, texture_scale_y, 0, 0}; + device()->SetVertexShaderConstantF(1, texture_scale, 1); + + ScopedRenderTargetRestorer render_target_restorer(device(), 0); + device()->SetRenderTarget(0, dst_surface); + device()->SetTexture(0, src_texture); + + D3DVIEWPORT9 viewport = { + 0, 0, + dst_size.width(), dst_size.height(), + 0, 1 + }; + device()->SetViewport(&viewport); + + if (d3d_utils::GetSize(src_texture) == dst_size) { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } else { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + } + device()->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + device()->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + + DrawScreenAlignedQuad(dst_size); + + // Clear surface references. + device()->SetTexture(0, NULL); + return true; +} + +void AcceleratedSurfaceTransformer::DrawScreenAlignedQuad( + const gfx::Size& size) { + const float target_size[4] = { size.width(), size.height(), 0, 0}; + + // Set the uniform shader constant |kRenderTargetSize|, which is bound + // to register c0. + device()->SetVertexShaderConstantF(0, target_size, 1); + + // We always send down the same vertices. The vertex program will take + // care of doing resolution-dependent position adjustment. + Vertex vertices[] = { + { -1, +1, 0.5f, 1, 0, 0 }, + { +1, +1, 0.5f, 1, 1, 0 }, + { +1, -1, 0.5f, 1, 1, 1 }, + { -1, -1, 0.5f, 1, 0, 1 } + }; + + device()->BeginScene(); + device()->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, + 2, + vertices, + sizeof(vertices[0])); + device()->EndScene(); + +} + +// Resize an RGB surface using repeated linear interpolation. +bool AcceleratedSurfaceTransformer::ResizeBilinear( + IDirect3DSurface9* src_surface, + const gfx::Rect& src_subrect, + IDirect3DSurface9* dst_surface) { + gfx::Size src_size = d3d_utils::GetSize(src_surface); + gfx::Size dst_size = d3d_utils::GetSize(dst_surface); + + if (src_size.IsEmpty() || dst_size.IsEmpty()) + return false; + + HRESULT hr = S_OK; + // Set up intermediate buffers needed for downsampling. + const int resample_count = + GetResampleCount(src_subrect, dst_size, src_size); + base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2]; + const gfx::Size half_size = + GetHalfSizeNoLessThan(src_subrect.size(), dst_size); + if (resample_count > 1) { + TRACE_EVENT0("gpu", "CreateTemporarySurface"); + if (!d3d_utils::CreateTemporaryLockableSurface(device(), + half_size, + temp_buffer[0].Receive())) + return false; + } + if (resample_count > 2) { + TRACE_EVENT0("gpu", "CreateTemporarySurface"); + const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, dst_size); + if (!d3d_utils::CreateTemporaryLockableSurface(device(), + quarter_size, + temp_buffer[1].Receive())) + return false; + } + + // Repeat downsampling the surface until its size becomes identical to + // |dst_size|. We keep the factor of each downsampling no more than two + // because using a factor more than two can introduce aliasing. + RECT read_rect = src_subrect.ToRECT(); + gfx::Size write_size = half_size; + int read_buffer_index = 1; + int write_buffer_index = 0; + for (int i = 0; i < resample_count; ++i) { + TRACE_EVENT0("gpu", "StretchRect"); + IDirect3DSurface9* read_buffer = + (i == 0) ? src_surface : temp_buffer[read_buffer_index]; + IDirect3DSurface9* write_buffer = + (i == resample_count - 1) ? dst_surface : + temp_buffer[write_buffer_index]; + RECT write_rect = gfx::Rect(write_size).ToRECT(); + hr = device()->StretchRect(read_buffer, + &read_rect, + write_buffer, + &write_rect, + D3DTEXF_LINEAR); + + if (FAILED(hr)) + return false; + read_rect = write_rect; + write_size = GetHalfSizeNoLessThan(write_size, dst_size); + std::swap(read_buffer_index, write_buffer_index); + } + + return true; +} + +bool AcceleratedSurfaceTransformer::TransformRGBToYV12( + IDirect3DTexture9* src_surface, + const gfx::Size& dst_size, + IDirect3DSurface9** dst_y, + IDirect3DSurface9** dst_u, + IDirect3DSurface9** dst_v) { + gfx::Size packed_y_size; + gfx::Size packed_uv_size; + if (!AllocYUVBuffers(dst_size, &packed_y_size, &packed_uv_size, + dst_y, dst_u, dst_v)) { + return false; + } + + if (device_supports_multiple_render_targets()) { + return TransformRGBToYV12_MRT(src_surface, + dst_size, + packed_y_size, + packed_uv_size, + *dst_y, + *dst_u, + *dst_v); + } else { + return TransformRGBToYV12_WithoutMRT(src_surface, + dst_size, + packed_y_size, + packed_uv_size, + *dst_y, + *dst_u, + *dst_v); + } +} + +bool AcceleratedSurfaceTransformer::AllocYUVBuffers( + const gfx::Size& dst_size, + gfx::Size* y_size, + gfx::Size* uv_size, + IDirect3DSurface9** dst_y, + IDirect3DSurface9** dst_u, + IDirect3DSurface9** dst_v) { + + // Y is full height, packed into 4 components. + *y_size = gfx::Size((dst_size.width() + 3) / 4, dst_size.height()); + + // U and V are half the size (rounded up) of Y. + *uv_size = gfx::Size((y_size->width() + 1) / 2, (y_size->height() + 1) / 2); + + if (!d3d_utils::CreateTemporaryLockableSurface(device(), *y_size, dst_y)) + return false; + if (!d3d_utils::CreateTemporaryLockableSurface(device(), *uv_size, dst_u)) + return false; + if (!d3d_utils::CreateTemporaryLockableSurface(device(), *uv_size, dst_v)) + return false; + return true; +} + +bool AcceleratedSurfaceTransformer::TransformRGBToYV12_MRT( + IDirect3DTexture9* src_surface, + const gfx::Size& dst_size, + const gfx::Size& packed_y_size, + const gfx::Size& packed_uv_size, + IDirect3DSurface9* dst_y, + IDirect3DSurface9* dst_u, + IDirect3DSurface9* dst_v) { + TRACE_EVENT0("gpu", "RGBToYV12_MRT"); + + ScopedRenderTargetRestorer color0_restorer(device(), 0); + ScopedRenderTargetRestorer color1_restorer(device(), 1); + + // Create an intermediate surface to hold the UUVV values. This is color + // target 1 for the first pass, and texture 0 for the second pass. Its + // values are not read afterwards. + base::win::ScopedComPtr<IDirect3DTexture9> uv_as_texture; + base::win::ScopedComPtr<IDirect3DSurface9> uv_as_surface; + if (!d3d_utils::CreateTemporaryRenderTargetTexture(device(), + packed_y_size, + uv_as_texture.Receive(), + uv_as_surface.Receive())) { + return false; + } + + // Clamping is required if (dst_size.width() % 8 != 0) or if + // (dst_size.height != 0), so we set it always. Both passes rely on this. + device()->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + device()->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + + ///////////////////////////////////////// + // Pass 1: RGB --(scaled)--> YYYY + UUVV + SetShaderCombo(RGB_TO_YV12_FAST__PASS_1_OF_2); + + // Enable bilinear filtering if scaling is required. The filtering will take + // place entirely in the first pass. + if (d3d_utils::GetSize(src_surface) != dst_size) { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + } else { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } + + device()->SetTexture(0, src_surface); + device()->SetRenderTarget(0, dst_y); + device()->SetRenderTarget(1, uv_as_surface); + DrawScreenAlignedQuad(dst_size); + + ///////////////////////////////////////// + // Pass 2: UUVV -> UUUU + VVVV + SetShaderCombo(RGB_TO_YV12_FAST__PASS_2_OF_2); + + // The second pass uses bilinear minification to achieve vertical scaling, + // so enable it always. + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + + device()->SetTexture(0, uv_as_texture); + device()->SetRenderTarget(0, dst_u); + device()->SetRenderTarget(1, dst_v); + DrawScreenAlignedQuad(packed_y_size); + + // Clear surface references. + device()->SetTexture(0, NULL); + return true; +} + +bool AcceleratedSurfaceTransformer::TransformRGBToYV12_WithoutMRT( + IDirect3DTexture9* src_surface, + const gfx::Size& dst_size, + const gfx::Size& packed_y_size, + const gfx::Size& packed_uv_size, + IDirect3DSurface9* dst_y, + IDirect3DSurface9* dst_u, + IDirect3DSurface9* dst_v) { + TRACE_EVENT0("gpu", "RGBToYV12_WithoutMRT"); + + ScopedRenderTargetRestorer color0_restorer(device(), 0); + + base::win::ScopedComPtr<IDirect3DTexture9> scaled_src_surface; + + // If scaling is requested, do it to a temporary texture. The MRT path + // gets a scale for free, so we need to support it here too (even though + // it's an extra operation). + if (d3d_utils::GetSize(src_surface) == dst_size) { + scaled_src_surface = src_surface; + } else { + base::win::ScopedComPtr<IDirect3DSurface9> dst_level0; + if (!d3d_utils::CreateTemporaryRenderTargetTexture( + device(), dst_size, + scaled_src_surface.Receive(), dst_level0.Receive())) { + return false; + } + + if (!Copy(src_surface, dst_level0, dst_size)) { + return false; + } + } + + // Input texture is the same for all three passes. + device()->SetTexture(0, scaled_src_surface); + + // Clamping is required if (dst_size.width() % 8 != 0) or if + // (dst_size.height != 0), so we set it always. All passes rely on this. + device()->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + device()->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + + ///////////////////// + // Pass 1: RGB -> Y. + SetShaderCombo(RGB_TO_YV12_SLOW__PASS_1_OF_3); + + // Pass 1 just needs point sampling. + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + + device()->SetRenderTarget(0, dst_y); + DrawScreenAlignedQuad(dst_size); + + // Passes 2 and 3 rely on bilinear minification to downsample U and V. + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + + ///////////////////// + // Pass 2: RGB -> U. + SetShaderCombo(RGB_TO_YV12_SLOW__PASS_2_OF_3); + device()->SetRenderTarget(0, dst_u); + DrawScreenAlignedQuad(dst_size); + + ///////////////////// + // Pass 3: RGB -> V. + SetShaderCombo(RGB_TO_YV12_SLOW__PASS_3_OF_3); + device()->SetRenderTarget(0, dst_v); + DrawScreenAlignedQuad(dst_size); + + // Clear surface references. + device()->SetTexture(0, NULL); + return true; +} + +IDirect3DDevice9* AcceleratedSurfaceTransformer::device() { + return device_; +} + +bool AcceleratedSurfaceTransformer::SetShaderCombo(ShaderCombo combo) { + // Compile shaders on first use, if needed. Normally the compilation should + // already have happened at Init() time, but test code might force + // us down an unusual path. + if (!CompileShaderCombo(combo)) + return false; + + HRESULT hr = device()->SetVertexShader(vertex_shaders_[combo]); + if (!SUCCEEDED(hr)) + return false; + hr = device()->SetPixelShader(pixel_shaders_[combo]); + if (!SUCCEEDED(hr)) + return false; + return true; +} diff --git a/ui/surface/accelerated_surface_transformer_win.h b/ui/surface/accelerated_surface_transformer_win.h index e01804a..97793c1 100644 --- a/ui/surface/accelerated_surface_transformer_win.h +++ b/ui/surface/accelerated_surface_transformer_win.h @@ -1,81 +1,184 @@ -// Copyright (c) 2012 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 UI_SURFACE_ACCELERATED_SURFACE_TRANSFORMER_WIN_H_
-#define UI_SURFACE_ACCELERATED_SURFACE_TRANSFORMER_WIN_H_
-
-#include <d3d9.h>
-
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/win/scoped_comptr.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/size.h"
-#include "ui/surface/surface_export.h"
-
-namespace gfx {
-class Size;
-class Rect;
-} // namespace gfx
-
-// Provides useful image filtering operations that are implemented
-// efficiently on DirectX9-class hardware using fragment programs.
-class SURFACE_EXPORT AcceleratedSurfaceTransformer {
- public:
- // Constructs an uninitialized surface transformer. Call Init() before
- // using the resulting object.
- AcceleratedSurfaceTransformer();
-
- // Init() initializes the transformer to operate on a device. This must be
- // called before any other method of this class, and it must be called
- // again after ReleaseAll() or DetachAll() before the class is used.
- //
- // Returns true if successful.
- bool Init(IDirect3DDevice9* device);
-
- // ReleaseAll() releases all direct3d resource references.
- void ReleaseAll();
-
- // DetachAll() leaks all direct3d resource references. This exists in order to
- // work around particular driver bugs, and should only be called at shutdown.
- // TODO(ncarter): Update the leak expectations before checkin.
- void DetachAll();
-
- // Draw a textured quad to a surface, flipping orientation in the y direction.
- bool CopyInverted(
- IDirect3DTexture9* src_texture,
- IDirect3DSurface9* dst_surface,
- const gfx::Size& dst_size);
-
- // Resize a surface using repeated bilinear interpolation.
- bool ResizeBilinear(
- IDirect3DSurface9* src_surface,
- const gfx::Rect& src_subrect,
- IDirect3DSurface9* dst_surface);
-
- private:
- enum ShaderCombo {
- SIMPLE_TEXTURE,
- NUM_SHADERS
- };
-
- // Set the active vertex and pixel shader combination.
- bool SetShaderCombo(ShaderCombo combo);
-
- // Intitializes a vertex and pixel shader combination from compiled bytecode.
- bool InitShaderCombo(const BYTE vertex_shader_instructions[],
- const BYTE pixel_shader_instructions[],
- ShaderCombo shader_combo_name);
-
- IDirect3DDevice9* device();
-
- base::win::ScopedComPtr<IDirect3DDevice9> device_;
- base::win::ScopedComPtr<IDirect3DVertexShader9> vertex_shaders_[NUM_SHADERS];
- base::win::ScopedComPtr<IDirect3DPixelShader9> pixel_shaders_[NUM_SHADERS];
- DISALLOW_COPY_AND_ASSIGN(AcceleratedSurfaceTransformer);
-};
-
-#endif // UI_SURFACE_ACCELERATED_SURFACE_TRANSFORMER_WIN_H_
\ No newline at end of file +// Copyright (c) 2012 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 UI_SURFACE_ACCELERATED_SURFACE_TRANSFORMER_WIN_H_ +#define UI_SURFACE_ACCELERATED_SURFACE_TRANSFORMER_WIN_H_ + +#include <d3d9.h> + +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/win/scoped_comptr.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/size.h" +#include "ui/surface/surface_export.h" + +namespace gfx { +class Size; +class Rect; +} // namespace gfx + +// Provides useful image filtering operations that are implemented +// efficiently on DirectX9-class hardware using fragment programs. +class SURFACE_EXPORT AcceleratedSurfaceTransformer { + public: + // Constructs an uninitialized surface transformer. Call Init() before + // using the resulting object. + AcceleratedSurfaceTransformer(); + + // Init() initializes the transformer to operate on a device. This must be + // called before any other method of this class, and it must be called + // again after ReleaseAll() or DetachAll() before the class is used. + // + // Returns true if successful. + bool Init(IDirect3DDevice9* device); + + // ReleaseAll() releases all direct3d resource references. + void ReleaseAll(); + + // DetachAll() leaks all direct3d resource references. This exists in order to + // work around particular driver bugs, and should only be called at shutdown. + // TODO(ncarter): Update the leak expectations before checkin. + void DetachAll(); + + // Draw a textured quad to a surface, flipping orientation in the y direction. + bool CopyInverted( + IDirect3DTexture9* src_texture, + IDirect3DSurface9* dst_surface, + const gfx::Size& dst_size); + + // Draw a textured quad to a surface. + bool Copy( + IDirect3DTexture9* src_texture, + IDirect3DSurface9* dst_surface, + const gfx::Size& dst_size); + + // Resize a surface using repeated bilinear interpolation. + bool ResizeBilinear( + IDirect3DSurface9* src_surface, + const gfx::Rect& src_subrect, + IDirect3DSurface9* dst_surface); + + // Color format conversion from RGB to planar YV12 (also known as YUV420). + // + // YV12 is effectively a twelve bit per pixel format consisting of a full- + // size y (luminance) plane and half-width, half-height u and v (blue and + // red chrominance) planes. This method will allocate three lockable surfaces, + // one for each plane, and return them via the arguments |dst_y|, |dst_u|, + // and |dst_v|. These surface will be created with an ARGB D3DFORMAT, but + // should be interpreted as the appropriate single-byte format when locking. + // + // The dimensions of the outputs (when interpreted as single-component data) + // are as follows: + // |dst_y| : width and height exactly |dst_size| + // |dst_u| : width and height are each half of |dst_size|, rounded up. + // |dst_v| : width and height are each half of |dst_size|, rounded up. + // + // If |src_texture|'s dimensions do not match |dst_size|, the source will be + // bilinearly interpolated during conversion. + // + // Returns true if successful. Caller must be certain to free the buffers + // even if this function returns false. + bool TransformRGBToYV12( + IDirect3DTexture9* src_texture, + const gfx::Size& dst_size, + IDirect3DSurface9** dst_y, + IDirect3DSurface9** dst_u, + IDirect3DSurface9** dst_v); + + private: + friend class AcceleratedSurfaceTransformerTest; + FRIEND_TEST_ALL_PREFIXES(AcceleratedSurfaceTransformerTest, Init); + + enum ShaderCombo { + ONE_TEXTURE, + RGB_TO_YV12_FAST__PASS_1_OF_2, + RGB_TO_YV12_FAST__PASS_2_OF_2, + RGB_TO_YV12_SLOW__PASS_1_OF_3, + RGB_TO_YV12_SLOW__PASS_2_OF_3, + RGB_TO_YV12_SLOW__PASS_3_OF_3, + NUM_SHADERS + }; + + // Efficient RGB->YV12 in two passes, but requires a device capable of writing + // multiple render targets at the same time. + // + // Returns true if successful. + bool TransformRGBToYV12_MRT( + IDirect3DTexture9* src_surface, + const gfx::Size& dst_size, + const gfx::Size& packed_y_size, + const gfx::Size& packed_uv_size, + IDirect3DSurface9* dst_y, + IDirect3DSurface9* dst_u, + IDirect3DSurface9* dst_v); + + // Slower, less efficient RGB->YV12; does not require the device to have + // multiple render target capability. Runs at about half speed of the fast + // path. + // + // Returns true if successful. + bool TransformRGBToYV12_WithoutMRT( + IDirect3DTexture9* src_surface, + const gfx::Size& dst_size, + const gfx::Size& packed_y_size, + const gfx::Size& packed_uv_size, + IDirect3DSurface9* dst_y, + IDirect3DSurface9* dst_u, + IDirect3DSurface9* dst_v); + + // Helper to allocate appropriately size YUV buffers, accounting for various + // roundings. The sizes of the buffers (in terms of ARGB pixels) are returned + // as |packed_y_size| and |packed_uv_size|. + // + // Returns true if successful. Caller must be certain to free the buffers + // even if this function returns false. + bool AllocYUVBuffers( + const gfx::Size& dst_size, + gfx::Size* packed_y_size, + gfx::Size* packed_uv_size, + IDirect3DSurface9** dst_y, + IDirect3DSurface9** dst_u, + IDirect3DSurface9** dst_v); + + bool CopyWithTextureScale( + IDirect3DTexture9* src_texture, + IDirect3DSurface9* dst_surface, + const gfx::Size& dst_size, + float texture_scale_x, + float texture_scale_y); + + // Set the active vertex and pixel shader combination. + // + // Returns true if successful. + bool SetShaderCombo(ShaderCombo combo); + + // Compiles a vertex and pixel shader combination, if not already compiled. + // + // Returns true if successful. + bool CompileShaderCombo(ShaderCombo shader_combo_name); + + bool DoInit(IDirect3DDevice9* device); + + void DrawScreenAlignedQuad(const gfx::Size& dst_size); + + bool device_supports_multiple_render_targets() const { + return device_supports_multiple_render_targets_; + } + + IDirect3DDevice9* device(); + + base::win::ScopedComPtr<IDirect3DDevice9> device_; + base::win::ScopedComPtr<IDirect3DVertexShader9> vertex_shaders_[NUM_SHADERS]; + base::win::ScopedComPtr<IDirect3DPixelShader9> pixel_shaders_[NUM_SHADERS]; + bool device_supports_multiple_render_targets_; + const BYTE* vertex_shader_sources_[NUM_SHADERS]; + const BYTE* pixel_shader_sources_[NUM_SHADERS]; + DISALLOW_COPY_AND_ASSIGN(AcceleratedSurfaceTransformer); +}; + +#endif // UI_SURFACE_ACCELERATED_SURFACE_TRANSFORMER_WIN_H_ diff --git a/ui/surface/accelerated_surface_transformer_win.hlsl b/ui/surface/accelerated_surface_transformer_win.hlsl index 0bd6fda..aa105ce 100644 --- a/ui/surface/accelerated_surface_transformer_win.hlsl +++ b/ui/surface/accelerated_surface_transformer_win.hlsl @@ -13,10 +13,17 @@ struct Vertex { texture t; sampler s; +extern uniform float2 kRenderTargetSize : c0; +extern uniform float2 kTextureScale : c1; + // @gyp_compile(vs_2_0, vsOneTexture) // // Passes a position and texture coordinate to the pixel shader. Vertex vsOneTexture(Vertex input) { + // Texture scale is typically just 1 (to do nothing) or -1 (to flip). + input.texCoord = ((2 * (input.texCoord - 0.5) * kTextureScale) + 1) / 2; + input.position.x += -1 / kRenderTargetSize.x; + input.position.y += 1 / kRenderTargetSize.y; return input; }; @@ -26,3 +33,268 @@ Vertex vsOneTexture(Vertex input) { float4 psOneTexture(float2 texCoord : TEXCOORD0) : COLOR0 { return tex2D(s, texCoord); }; + +// Return |value| rounded up to the nearest multiple of |multiple|. +float alignTo(float value, float multiple) { + // |multiple| is usually a compile-time constant; this check allows + // the compiler to avoid the fmod when possible. + if (multiple == 1) + return value; + + // Biasing the value provides numeric stability. We expect |value| to + // be an integer; this prevents 4.001 from being rounded up to 8. + float biased_value = value - 0.5; + return biased_value + multiple - fmod(biased_value, multiple); +} + +float4 packForByteOrder(float4 value) { + return value.bgra; +} + +// Adjust the input vertex to address the correct range of texels. This depends +// on the value of the shader constant |kRenderTargetSize|, as well as an +// alignment factor |align| that effectively specifies the footprint of the +// texel samples done by this shader pass, and is used to correct when that +// footprint size doesn't align perfectly with the actual input size. +Vertex adjustForAlignmentAndPacking(Vertex vtx, float2 align) { + float src_width = kRenderTargetSize.x; + float src_height = kRenderTargetSize.y; + + // Because our caller expects to be sampling |align.x| many pixels from src at + // a time, if src's width isn't evenly divisible by |align.x|, it is necessary + // to pretend that the source is slightly bigger than it is. + float bloated_src_width = alignTo(src_width, align.x); + float bloated_src_height = alignTo(src_height, align.y); + + // When bloated_src_width != src_width, we'll adjust the texture coordinates + // to sample past the edge of the vtx; clamping will produce extra copies of + // the last row. + float texture_x_scale = bloated_src_width / src_width; + float texture_y_scale = bloated_src_height / src_height; + + // Adjust positions so that we're addressing full fragments in the output, per + // the top-left filling convention. The shifts would be equivalent to + // 1/dst_width and 1/dst_height, if we were to calculate those explicitly. + vtx.position.x -= align.x / bloated_src_width; + vtx.position.y += align.y / bloated_src_height; + + // Apply the texture scale + vtx.texCoord.x *= texture_x_scale; + vtx.texCoord.y *= texture_y_scale; + + return vtx; +} + +/////////////////////////////////////////////////////////////////////// +// RGB24 to YV12 in two passes; writing two 8888 targets each pass. +// +// YV12 is full-resolution luma and half-resolution blue/red chroma. +// +// (original) +// XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB +// XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB +// XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB +// XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB +// XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB +// XRGB XRGB XRGB XRGB XRGB XRGB XRGB XRGB +// | +// | (y plane) (temporary) +// | YYYY YYYY UVUV UVUV +// +--> { YYYY YYYY + UVUV UVUV } +// YYYY YYYY UVUV UVUV +// First YYYY YYYY UVUV UVUV +// pass YYYY YYYY UVUV UVUV +// YYYY YYYY UVUV UVUV +// | +// | (u plane) (v plane) +// Second | UUUU VVVV +// pass +--> { UUUU + VVVV } +// UUUU VVVV +// +/////////////////////////////////////////////////////////////////////// + +// Phase one of RGB24->YV12 conversion: vsFetch4Pixels/psConvertRGBtoY8UV44 +// +// @gyp_compile(vs_2_0, vsFetch4Pixels) +// @gyp_compile(ps_2_0, psConvertRGBtoY8UV44) +// +// Writes four source pixels at a time to a full-size Y plane and a half-width +// interleaved UV plane. After execution, the Y plane is complete but the UV +// planes still need to be de-interleaved and vertically scaled. +// +void vsFetch4Pixels(in Vertex vertex, + out float4 position : POSITION, + out float2 texCoord0 : TEXCOORD0, + out float2 texCoord1 : TEXCOORD1, + out float2 texCoord2 : TEXCOORD2, + out float2 texCoord3 : TEXCOORD3) { + Vertex adjusted = adjustForAlignmentAndPacking(vertex, float2(4, 1)); + + // Set up four taps, aligned to texel centers if the src's true size is + // |kRenderTargetSize|, and doing bilinear interpolation otherwise. + float2 one_texel_x = float2(1 / kRenderTargetSize.x, 0); + position = adjusted.position; + texCoord0 = adjusted.texCoord - 1.5f * one_texel_x; + texCoord1 = adjusted.texCoord - 0.5f * one_texel_x; + texCoord2 = adjusted.texCoord + 0.5f * one_texel_x; + texCoord3 = adjusted.texCoord + 1.5f * one_texel_x; +}; + +struct YV16QuadPixel +{ + float4 YYYY : COLOR0; + float4 UUVV : COLOR1; +}; + +// Color conversion constants. +static const float3x1 rgb_to_y = float3x1( +0.257f, +0.504f, +0.098f ); +static const float3x1 rgb_to_u = float3x1( -0.148f, -0.291f, +0.439f ); +static const float3x1 rgb_to_v = float3x1( +0.439f, -0.368f, -0.071f ); +static const float y_bias = 0.0625f; +static const float uv_bias = 0.5f; + +YV16QuadPixel psConvertRGBtoY8UV44(float2 texCoord0 : TEXCOORD0, + float2 texCoord1 : TEXCOORD1, + float2 texCoord2 : TEXCOORD2, + float2 texCoord3 : TEXCOORD3) { + // Load the four texture samples into a matrix. + float4x3 rgb_quad_pixel = float4x3(tex2D(s, texCoord0).rgb, + tex2D(s, texCoord1).rgb, + tex2D(s, texCoord2).rgb, + tex2D(s, texCoord3).rgb); + + // RGB -> Y conversion (x4). + float4 yyyy = mul(rgb_quad_pixel, rgb_to_y) + y_bias; + + // Average adjacent texture samples while converting RGB->UV. This is the same + // as color converting then averaging, but slightly less math. These values + // will be in the range [-0.439f, +0.439f] and still need to have the bias + // term applied. + float2x3 rgb_double_pixel = float2x3(rgb_quad_pixel[0] + rgb_quad_pixel[1], + rgb_quad_pixel[2] + rgb_quad_pixel[3]); + float2 uu = mul(rgb_double_pixel, rgb_to_u / 2); + float2 vv = mul(rgb_double_pixel, rgb_to_v / 2); + + // Package the result to account for BGRA byte ordering. + YV16QuadPixel result; + result.YYYY = packForByteOrder(yyyy); + result.UUVV.xyzw = float4(uu, vv) + uv_bias; // Apply uv bias. + return result; +}; + +// Phase two of RGB24->YV12 conversion: vsFetch2Pixels/psConvertUV44toU2V2 +// +// @gyp_compile(vs_2_0, vsFetch2Pixels) +// @gyp_compile(ps_2_0, psConvertUV44toU2V2) +// +// Deals with UV only. Input is interleaved UV pixels, already scaled +// horizontally, packed two per RGBA texel. Output is two color planes U and V, +// packed four to a RGBA pixel. +// +// Vertical scaling happens via a half-texel offset and bilinear interpolation +// during texture sampling. +void vsFetch2Pixels(in Vertex vertex, + out float4 position : POSITION, + out float2 texCoord0 : TEXCOORD0, + out float2 texCoord1 : TEXCOORD1) { + // We fetch two texels in the horizontal direction, and scale by 2 in the + // vertical direction. + Vertex adjusted = adjustForAlignmentAndPacking(vertex, float2(2, 2)); + + // Setup the two texture coordinates. No need to adjust texCoord.y; it's + // already at the mid-way point between the two rows. Horizontally, we'll + // fetch two texels so that we have enough data to fill our output. + float2 one_texel_x = float2(1 / kRenderTargetSize.x, 0); + position = adjusted.position; + texCoord0 = adjusted.texCoord - 0.5f * one_texel_x; + texCoord1 = adjusted.texCoord + 0.5f * one_texel_x; +}; + +struct UV8QuadPixel { + float4 UUUU : COLOR0; + float4 VVVV : COLOR1; +}; + +UV8QuadPixel psConvertUV44toU2V2(float2 texCoord0 : TEXCOORD0, + float2 texCoord1 : TEXCOORD1) { + // We're just sampling two pixels and unswizzling them. There's no need to do + // vertical scaling with math, since bilinear interpolation in the sampler + // takes care of that. + float4 lo_uuvv = tex2D(s, texCoord0); + float4 hi_uuvv = tex2D(s, texCoord1); + UV8QuadPixel result; + result.UUUU = packForByteOrder(float4(lo_uuvv.xy, hi_uuvv.xy)); + result.VVVV = packForByteOrder(float4(lo_uuvv.zw, hi_uuvv.zw)); + return result; +}; + + +/////////////////////////////////////////////////////////////////////// +// RGB24 to YV12 in three passes, without MRT: one pass per output color plane. +// vsFetch4Pixels is the common vertex shader for all three passes. +// +// Note that this technique will not do full bilinear filtering on its RGB +// input (you'd get correctly filtered Y, but aliasing in U and V). +// +// Pass 1: vsFetch4Pixels + psConvertRGBToY +// Pass 2: vsFetch4Pixels_Scale2 + psConvertRGBToU +// Pass 3: vsFetch4Pixels_Scale2 + psConvertRGBToV +// +// @gyp_compile(vs_2_0, vsFetch4Pixels_Scale2) +// @gyp_compile(ps_2_0, psConvertRGBtoY) +// @gyp_compile(ps_2_0, psConvertRGBtoU) +// @gyp_compile(ps_2_0, psConvertRGBtoV) +// +/////////////////////////////////////////////////////////////////////// +void vsFetch4Pixels_Scale2(in Vertex vertex, + out float4 position : POSITION, + out float2 texCoord0 : TEXCOORD0, + out float2 texCoord1 : TEXCOORD1, + out float2 texCoord2 : TEXCOORD2, + out float2 texCoord3 : TEXCOORD3) { + Vertex adjusted = adjustForAlignmentAndPacking(vertex, float2(8, 2)); + + // Set up four taps, each of which samples a 2x2 texel quad at the midpoint. + float2 one_texel_x = float2(1 / kRenderTargetSize.x, 0); + position = adjusted.position; + texCoord0 = adjusted.texCoord - 3 * one_texel_x; + texCoord1 = adjusted.texCoord - 1 * one_texel_x; + texCoord2 = adjusted.texCoord + 1 * one_texel_x; + texCoord3 = adjusted.texCoord + 3 * one_texel_x; +}; + +// RGB -> Y, four samples at a time. +float4 psConvertRGBtoY(float2 texCoord0 : TEXCOORD0, + float2 texCoord1 : TEXCOORD1, + float2 texCoord2 : TEXCOORD2, + float2 texCoord3 : TEXCOORD3) : COLOR0 { + float4x3 rgb_quad_pixel = float4x3(tex2D(s, texCoord0).rgb, + tex2D(s, texCoord1).rgb, + tex2D(s, texCoord2).rgb, + tex2D(s, texCoord3).rgb); + return packForByteOrder(mul(rgb_quad_pixel, rgb_to_y) + y_bias); +} + +// RGB -> U, four samples at a time. +float4 psConvertRGBtoU(float2 texCoord0 : TEXCOORD0, + float2 texCoord1 : TEXCOORD1, + float2 texCoord2 : TEXCOORD2, + float2 texCoord3 : TEXCOORD3) : COLOR0 { + float4x3 rgb_quad_pixel = float4x3(tex2D(s, texCoord0).rgb, + tex2D(s, texCoord1).rgb, + tex2D(s, texCoord2).rgb, + tex2D(s, texCoord3).rgb); + return packForByteOrder(mul(rgb_quad_pixel, rgb_to_u) + uv_bias); +} + +// RGB -> V, four samples at a time. +float4 psConvertRGBtoV(float2 texCoord0 : TEXCOORD0, + float2 texCoord1 : TEXCOORD1, + float2 texCoord2 : TEXCOORD2, + float2 texCoord3 : TEXCOORD3) : COLOR0 { + float4x3 rgb_quad_pixel = float4x3(tex2D(s, texCoord0).rgb, + tex2D(s, texCoord1).rgb, + tex2D(s, texCoord2).rgb, + tex2D(s, texCoord3).rgb); + return packForByteOrder(mul(rgb_quad_pixel, rgb_to_v) + uv_bias); +} diff --git a/ui/surface/accelerated_surface_transformer_win_unittest.cc b/ui/surface/accelerated_surface_transformer_win_unittest.cc index 5d432ef..ed5b606 100644 --- a/ui/surface/accelerated_surface_transformer_win_unittest.cc +++ b/ui/surface/accelerated_surface_transformer_win_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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. @@ -6,13 +6,21 @@ #include <random> #include "base/basictypes.h" +#include "base/file_util.h" #include "base/hash.h" #include "base/scoped_native_library.h" #include "base/stringprintf.h" +#include "base/time.h" #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" +#include "media/base/simd/convert_rgb_to_yuv.h" +#include "media/base/yuv_convert.h" +#include "skia/ext/image_operations.h" #include "testing/gtest/include/gtest/gtest-param-test.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/rect.h" #include "ui/surface/accelerated_surface_transformer_win.h" #include "ui/surface/accelerated_surface_win.h" @@ -23,14 +31,67 @@ namespace d3d_utils = ui_surface_d3d9_utils; using base::win::ScopedComPtr; using std::uniform_int_distribution; -// Provides a reference rasterizer (all rendering done by software emulation) -// Direct3D device, for use by unit tests. +namespace { + +// Debug flag, useful when hacking on tests. +const bool kDumpImagesOnFailure = false; + +SkBitmap ToSkBitmap(IDirect3DSurface9* surface, bool is_single_channel) { + D3DLOCKED_RECT locked_rect; + EXPECT_HRESULT_SUCCEEDED( + surface->LockRect(&locked_rect, NULL, D3DLOCK_READONLY)); + + SkBitmap result; + gfx::Size size = d3d_utils::GetSize(surface); + if (is_single_channel) + size = gfx::Size(size.width() * 4, size.height()); + result.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); + result.setIsOpaque(true); + result.allocPixels(); + result.lockPixels(); + for (int y = 0; y < size.height(); ++y) { + uint8* row8 = reinterpret_cast<uint8*>(locked_rect.pBits) + + (y * locked_rect.Pitch); + if (is_single_channel) { + for (int x = 0; x < size.width(); ++x) { + *result.getAddr32(x, y) = SkColorSetRGB(row8[x], row8[x], row8[x]); + } + } else { + uint32* row32 = reinterpret_cast<uint32*>(row8); + for (int x = 0; x < size.width(); ++x) { + *result.getAddr32(x, y) = row32[x] | 0xFF000000; + } + } + } + result.unlockPixels(); + result.setImmutable(); + surface->UnlockRect(); + return result; +} + +bool WritePNGFile(const SkBitmap& bitmap, const FilePath& file_path) { + std::vector<unsigned char> png_data; + const bool discard_transparency = true; + if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, + discard_transparency, + &png_data) && + file_util::CreateDirectory(file_path.DirName())) { + char* data = reinterpret_cast<char*>(&png_data[0]); + int size = static_cast<int>(png_data.size()); + return file_util::WriteFile(file_path, data, size) == size; + } + return false; +} + +} // namespace + +// Test fixture for AcceleratedSurfaceTransformer. // // This class is parameterized so that it runs only on Vista+. See // WindowsVersionIfVistaOrBetter() for details on this works. class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { public: - AcceleratedSurfaceTransformerTest() {}; + AcceleratedSurfaceTransformerTest() : color_error_tolerance_(0) {}; IDirect3DDevice9Ex* device() { return device_.get(); } @@ -79,6 +140,15 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { device()->Present(0, 0, 0, 0)); } + void WarnOnMissingFeatures(AcceleratedSurfaceTransformer* gpu_ops) { + // Prints a single warning line if some tests are feature-dependent + // and the feature is not supported by the current GPU. + if (!gpu_ops->device_supports_multiple_render_targets()) { + LOG(WARNING) << "MRT not supported, some tests will be skipped. " + << GetAdapterInfo(); + } + } + // Locks and fills a surface with a checkerboard pattern where the colors // are random but the total image pattern is horizontally and vertically // symmetric. @@ -94,8 +164,8 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { ASSERT_EQ(0, locked_rect.Pitch % sizeof(DWORD)); int pitch = locked_rect.Pitch / sizeof(DWORD); - for (int y = 0; y <= size.height() / 2; y += checker_square_size) { - for (int x = 0; x <= size.width() / 2; x += checker_square_size) { + for (int y = 0; y < (size.height() + 1) / 2; y += checker_square_size) { + for (int x = 0; x < (size.width() + 1) / 2; x += checker_square_size) { DWORD color = RandomColor(); int y_limit = std::min(size.height() / 2, y + checker_square_size - 1); int x_limit = std::min(size.width() / 2, x + checker_square_size - 1); @@ -154,7 +224,7 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { max_error = std::max(max_error, std::abs(static_cast<int>(a[i]) - b[i])); - if (max_error <= kAbsoluteColorErrorTolerance) + if (max_error <= color_error_tolerance()) return true; std::string expected_color = @@ -163,8 +233,19 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { StringPrintf("%3d, %3d, %3d, %3d", b[0], b[1], b[2], b[3]); EXPECT_EQ(expected_color, actual_color) << "Componentwise color difference was " - << max_error << "; max allowed is " << kAbsoluteColorErrorTolerance; + << max_error << "; max allowed is " << color_error_tolerance(); + + return false; + } +bool AssertSameColor(uint8 color_a, uint8 color_b) { + if (color_a == color_b) + return true; + int max_error = std::abs((int) color_a - (int) color_b); + if (max_error <= color_error_tolerance()) + return true; + ADD_FAILURE() << "Colors not equal: " << StringPrintf("0x%x", color_a) + << " vs. " << StringPrintf("0x%x", color_b); return false; } @@ -239,12 +320,18 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { } protected: - static const int kAbsoluteColorErrorTolerance = 5; - DWORD RandomColor() { return random_dword_(rng_); } + void set_color_error_tolerance(int value) { + color_error_tolerance_ = value; + } + + int color_error_tolerance() { + return color_error_tolerance_; + } + void DoResizeBilinearTest(AcceleratedSurfaceTransformer* gpu_ops, const gfx::Size& src_size, const gfx::Size& dst_size, @@ -256,6 +343,8 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { dst_size.width(), dst_size.height(), checkerboard_size)); + set_color_error_tolerance(4); + base::win::ScopedComPtr<IDirect3DSurface9> src, dst; ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface( device(), src_size, src.Receive())) @@ -271,29 +360,211 @@ class AcceleratedSurfaceTransformerTest : public testing::TestWithParam<int> { AssertSymmetry(dst, dst_size); } + void CreateRandomCheckerboardTexture( + const gfx::Size& size, + int checkerboard_size, + IDirect3DSurface9** reference_surface, + IDirect3DTexture9** result) { + base::win::ScopedComPtr<IDirect3DSurface9> dst; + ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(device(), size, + reference_surface)); + ASSERT_TRUE(d3d_utils::CreateTemporaryRenderTargetTexture(device(), size, + result, dst.Receive())); + FillRandomCheckerboard(*reference_surface, size, checkerboard_size); + ASSERT_HRESULT_SUCCEEDED( + device()->StretchRect( + *reference_surface, NULL, dst, NULL, D3DTEXF_NONE)); + } + + void AssertSame(int width_in_bytes, int height, uint8* reference, + IDirect3DSurface9* lockable) { + BeforeLockWorkaround(); + + D3DLOCKED_RECT locked_rect; + ASSERT_HRESULT_SUCCEEDED( + lockable->LockRect(&locked_rect, NULL, D3DLOCK_READONLY)); + uint8* actual = reinterpret_cast<uint8*>(locked_rect.pBits); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width_in_bytes; ++x) { + if (!AssertSameColor(reference[y * width_in_bytes + x], + actual[y * locked_rect.Pitch + x])) { + lockable->UnlockRect(); + GTEST_FAIL() << "At pixel (" << x << ", " << y << ")"; + } + } + } + lockable->UnlockRect(); + } + void DoCopyInvertedTest(AcceleratedSurfaceTransformer* gpu_ops, const gfx::Size& size) { SCOPED_TRACE( StringPrintf("CopyInverted @ %dx%d", size.width(), size.height())); - base::win::ScopedComPtr<IDirect3DSurface9> checkerboard, src, dst; - base::win::ScopedComPtr<IDirect3DTexture9> src_texture; - ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(device(), size, - checkerboard.Receive())) << "Could not create src render target";; - ASSERT_TRUE(d3d_utils::CreateTemporaryRenderTargetTexture(device(), size, - src_texture.Receive(), src.Receive())) - << "Could not create src texture."; - ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(device(), size, + set_color_error_tolerance(0); + + base::win::ScopedComPtr<IDirect3DSurface9> dst, reference_pattern; + base::win::ScopedComPtr<IDirect3DTexture9> src; + + CreateRandomCheckerboardTexture(size, 1, reference_pattern.Receive(), + src.Receive()); + + // Alloc a slightly larger image 75% of the time, to test that the + // viewport is set properly. + const int kAlign = 4; + gfx::Size alloc_size((size.width() + kAlign - 1) / kAlign * kAlign, + (size.height() + kAlign - 1) / kAlign * kAlign); + + ASSERT_TRUE(d3d_utils::CreateTemporaryLockableSurface(device(), alloc_size, dst.Receive())) << "Could not create dst render target."; - FillRandomCheckerboard(checkerboard, size, 1); - ASSERT_HRESULT_SUCCEEDED( - device()->StretchRect(checkerboard, NULL, src, NULL, D3DTEXF_NONE)); - ASSERT_TRUE(gpu_ops->CopyInverted(src_texture, dst, size)); - AssertIsInvertedCopy(size, checkerboard, dst); + ASSERT_TRUE(gpu_ops->CopyInverted(src, dst, size)); + AssertIsInvertedCopy(size, reference_pattern, dst); + } + + + void DoYUVConversionTest(AcceleratedSurfaceTransformer* gpu_ops, + const gfx::Size& src_size, + int checkerboard_size) { + // Test the non-MRT implementation, and the MRT implementation as well + // (if supported by the device). + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(gpu_ops, src_size, src_size, + checkerboard_size, false)); + if (gpu_ops->device_supports_multiple_render_targets()) { + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(gpu_ops, src_size, src_size, + checkerboard_size, true)); + } + } + + void DoYUVConversionScaleTest(AcceleratedSurfaceTransformer* gpu_ops, + const gfx::Size& src_size, + const gfx::Size& dst_size) { + // Test the non-MRT implementation, and the MRT implementation as well + // (if supported by the device). + if (gpu_ops->device_supports_multiple_render_targets()) { + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(gpu_ops, src_size, dst_size, 4, true)); + } + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(gpu_ops, src_size, dst_size, 4, false)); } + void DoYUVConversionTest(AcceleratedSurfaceTransformer* gpu_ops, + const gfx::Size& src_size, + const gfx::Size& dst_size, + int checkerboard_size, + boolean use_multi_render_targets) { + SCOPED_TRACE( + StringPrintf("YUV Converting %dx%d at checkerboard size of %d; MRT %s", + src_size.width(), src_size.height(), + checkerboard_size, + use_multi_render_targets ? "enabled" : "disabled")); + + + base::win::ScopedComPtr<IDirect3DTexture9> src; + base::win::ScopedComPtr<IDirect3DSurface9> reference; + base::win::ScopedComPtr<IDirect3DSurface9> dst_y, dst_u, dst_v; + + // TODO(ncarter): Use a better error metric that measures aggregate error + // rather than simply max error. There seems to be slightly more error at + // higher resolutions, maybe due to precision issues during rasterization + // (or maybe more pixels = more test trials). Results are usually to an + // error of 1, but we must use a tolerance of 3. + set_color_error_tolerance(3); + CreateRandomCheckerboardTexture( + src_size, checkerboard_size, reference.Receive(), src.Receive()); + + gfx::Size packed_y_size, packed_uv_size; + + ASSERT_TRUE(gpu_ops->AllocYUVBuffers(dst_size, + &packed_y_size, + &packed_uv_size, + dst_y.Receive(), + dst_u.Receive(), + dst_v.Receive())); + + // Actually do the conversion. + if (use_multi_render_targets) { + ASSERT_TRUE(gpu_ops->TransformRGBToYV12_MRT(src, + dst_size, + packed_y_size, + packed_uv_size, + dst_y, + dst_u, + dst_v)); + } else { + ASSERT_TRUE(gpu_ops->TransformRGBToYV12_WithoutMRT(src, + dst_size, + packed_y_size, + packed_uv_size, + dst_y, + dst_u, + dst_v)); + } + + // UV size (in bytes/samples) is half, rounded up. + gfx::Size uv_size((dst_size.width() + 1) / 2, + (dst_size.height() + 1) / 2); + + // Generate a reference bitmap by calling a software implementation. + SkBitmap reference_rgb = ToSkBitmap(reference, false); + SkBitmap reference_rgb_scaled; + if (dst_size == src_size) { + reference_rgb_scaled = reference_rgb; + } else { + // We'll call Copy to do the bilinear scaling if needed. + base::win::ScopedComPtr<IDirect3DSurface9> reference_scaled; + ASSERT_TRUE( + d3d_utils::CreateTemporaryLockableSurface( + device(), dst_size, reference_scaled.Receive())); + ASSERT_TRUE( + gpu_ops->Copy(src, reference_scaled, dst_size)); + BeforeLockWorkaround(); + reference_rgb_scaled = ToSkBitmap(reference_scaled, false); + } + + scoped_ptr<uint8[]> reference_y(new uint8[dst_size.GetArea()]); + scoped_ptr<uint8[]> reference_u(new uint8[uv_size.GetArea()]); + scoped_ptr<uint8[]> reference_v(new uint8[uv_size.GetArea()]); + reference_rgb_scaled.lockPixels(); + media::ConvertRGB32ToYUV_SSE2_Reference( + reinterpret_cast<uint8*>(reference_rgb_scaled.getAddr32(0, 0)), + &reference_y[0], + &reference_u[0], + &reference_v[0], + dst_size.width(), + dst_size.height(), + reference_rgb_scaled.rowBytes(), + dst_size.width(), + uv_size.width()); + reference_rgb_scaled.unlockPixels(); + + // Check for equality of the reference and the actual. + AssertSame(dst_size.width(), dst_size.height(), &reference_y[0], dst_y); + AssertSame(uv_size.width(), uv_size.height(), &reference_u[0], dst_u); + AssertSame(uv_size.width(), uv_size.height(), &reference_v[0], dst_v); + + if (kDumpImagesOnFailure && HasFatalFailure()) { + // Note that this will dump the full u and v buffers, including + // extra columns added due to packing. That means up to 7 extra + // columns for uv, and up to 3 extra columns for y. + WritePNGFile(reference_rgb, + FilePath(FILE_PATH_LITERAL("test_fail_src.png"))); + WritePNGFile(reference_rgb_scaled, + FilePath(FILE_PATH_LITERAL("test_fail_src_scaled.png"))); + WritePNGFile(ToSkBitmap(dst_y, true), + FilePath(FILE_PATH_LITERAL("test_fail_y.png"))); + WritePNGFile(ToSkBitmap(dst_u, true), + FilePath(FILE_PATH_LITERAL("test_fail_u.png"))); + WritePNGFile(ToSkBitmap(dst_v, true), + FilePath(FILE_PATH_LITERAL("test_fail_v.png"))); + } + } + + int color_error_tolerance_; uniform_int_distribution<DWORD> random_dword_; std::mt19937 rng_; base::ScopedNativeLibrary d3d_module_; @@ -305,6 +576,8 @@ TEST_P(AcceleratedSurfaceTransformerTest, Init) { SCOPED_TRACE(GetAdapterInfo()); AcceleratedSurfaceTransformer gpu_ops; ASSERT_TRUE(gpu_ops.Init(device())); + + WarnOnMissingFeatures(&gpu_ops); }; // Fails on some bots because Direct3D isn't allowed. @@ -341,7 +614,6 @@ TEST_P(AcceleratedSurfaceTransformerTest, CopyInverted) { } } - // Fails on some bots because Direct3D isn't allowed. // Fails on other bots because of ResizeBilinear symmetry failures. // Should pass, at least, on NVIDIA Quadro 600. @@ -363,10 +635,14 @@ TEST_P(AcceleratedSurfaceTransformerTest, MixedOperations) { ASSERT_NO_FATAL_FAILURE( DoResizeBilinearTest(&t, gfx::Size(256, 256), gfx::Size(64, 64), 5)); ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&t, gfx::Size(128, 128), 1)); + ASSERT_NO_FATAL_FAILURE( DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(3, 3), 1)); ASSERT_NO_FATAL_FAILURE( DoCopyInvertedTest(&t, gfx::Size(1412, 124))); ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&t, gfx::Size(100, 200), 1)); + ASSERT_NO_FATAL_FAILURE( DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(257, 257), 1)); ASSERT_NO_FATAL_FAILURE( DoResizeBilinearTest(&t, gfx::Size(255, 255), gfx::Size(257, 257), 2)); @@ -380,6 +656,8 @@ TEST_P(AcceleratedSurfaceTransformerTest, MixedOperations) { ASSERT_NO_FATAL_FAILURE( DoCopyInvertedTest(&t, gfx::Size(1521, 3))); ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&t, gfx::Size(140, 181), 1)); + ASSERT_NO_FATAL_FAILURE( DoResizeBilinearTest(&t, gfx::Size(150, 256), gfx::Size(126, 256), 1)); ASSERT_NO_FATAL_FAILURE( DoCopyInvertedTest(&t, gfx::Size(33, 712))); @@ -425,6 +703,12 @@ TEST_P(AcceleratedSurfaceTransformerTest, LargeSurfaces) { DoCopyInvertedTest(&gpu_ops, gfx::Size(w, lo))); ASSERT_NO_FATAL_FAILURE( DoCopyInvertedTest(&gpu_ops, gfx::Size(lo, h))); + + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(w, lo), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(lo, h), 1)); + } // Exercises ResizeBilinear with random minification cases where the @@ -440,8 +724,8 @@ TEST_P(AcceleratedSurfaceTransformerTest, MinifyUniform) { AcceleratedSurfaceTransformer gpu_ops; ASSERT_TRUE(gpu_ops.Init(device())); - int dims[] = { 21, 63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257}; - int checkerboards[] = {1, 2, 3, 9}; + const int dims[] = {21, 63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257}; + const int checkerboards[] = {1, 2, 3, 9}; uniform_int_distribution<int> dim(0, arraysize(dims) - 1); uniform_int_distribution<int> checkerboard(0, arraysize(checkerboards) - 1); @@ -481,8 +765,8 @@ TEST_P(AcceleratedSurfaceTransformerTest, DISABLED_MagnifyUniform) { AcceleratedSurfaceTransformer gpu_ops; ASSERT_TRUE(gpu_ops.Init(device())); - int dims[] = {63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257}; - int checkerboards[] = {1, 2, 3, 9}; + const int dims[] = {63, 64, 65, 99, 127, 128, 129, 192, 255, 256, 257}; + const int checkerboards[] = {1, 2, 3, 9}; uniform_int_distribution<int> dim(0, arraysize(dims) - 1); uniform_int_distribution<int> checkerboard(0, arraysize(checkerboards) - 1); @@ -507,6 +791,87 @@ TEST_P(AcceleratedSurfaceTransformerTest, DISABLED_MagnifyUniform) { } }; +TEST_P(AcceleratedSurfaceTransformerTest, RGBtoYUV) { + SeedRandom("RGBtoYUV"); + + AcceleratedSurfaceTransformer gpu_ops; + ASSERT_TRUE(gpu_ops.Init(device())); + + // Start with some easy-to-debug cases. A checkerboard size of 1 is the + // best test, but larger checkerboard sizes give more insight into where + // a bug might be. + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(32, 32), 4)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(32, 32), 2)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(32, 32), 3)); + + // All cases of width (mod 8) and height (mod 8), using 1x1 checkerboard. + for (int w = 32; w < 40; ++w) { + for (int h = 32; h < 40; ++h) { + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(w, h), 1)); + } + } + + // All the very small sizes which require the most shifting in the + // texture coordinates when doing alignment. + for (int w = 1; w <= 9; ++w) { + for (int h = 1; h <= 9; ++h) { + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(w, h), 1)); + } + } + + // Random medium dimensions. + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(10, 142), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(124, 333), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(853, 225), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(231, 412), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(512, 128), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(1024, 768), 1)); + + // Common video/monitor resolutions + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(800, 768), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(1024, 768), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(1280, 720), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(1280, 720), 2)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(1920, 1080), 1)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(1920, 1080), 2)); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionTest(&gpu_ops, gfx::Size(2048, 1536), 1)); +} + +TEST_P(AcceleratedSurfaceTransformerTest, RGBtoYUVScaled) { + SeedRandom("RGBtoYUVScaled"); + + AcceleratedSurfaceTransformer gpu_ops; + ASSERT_TRUE(gpu_ops.Init(device())); + + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionScaleTest(&gpu_ops, gfx::Size(32, 32), gfx::Size(64, 64))); + + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionScaleTest(&gpu_ops, gfx::Size(32, 32), gfx::Size(16, 16))); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionScaleTest(&gpu_ops, gfx::Size(32, 32), gfx::Size(24, 24))); + ASSERT_NO_FATAL_FAILURE( + DoYUVConversionScaleTest(&gpu_ops, gfx::Size(32, 32), gfx::Size(48, 48))); +} + namespace { // Used to suppress test on Windows versions prior to Vista. diff --git a/ui/surface/d3d9_utils_win.cc b/ui/surface/d3d9_utils_win.cc index d5cab8e..aa18032 100644 --- a/ui/surface/d3d9_utils_win.cc +++ b/ui/surface/d3d9_utils_win.cc @@ -1,119 +1,135 @@ -// Copyright (c) 2012 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 "ui/surface/d3d9_utils_win.h"
-
-#include "base/file_path.h"
-#include "base/scoped_native_library.h"
-#include "base/win/scoped_comptr.h"
-#include "ui/gfx/size.h"
-
-namespace {
-
-const wchar_t kD3D9ModuleName[] = L"d3d9.dll";
-const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex";
-typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT sdk_version,
- IDirect3D9Ex **d3d);
-} // namespace
-
-namespace ui_surface_d3d9_utils {
-
-bool LoadD3D9(base::ScopedNativeLibrary* storage) {
- storage->Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL));
- return storage->is_valid();
-}
-
-bool CreateDevice(const base::ScopedNativeLibrary& d3d_module,
- D3DDEVTYPE device_type,
- uint32 presentation_interval,
- IDirect3DDevice9Ex** device) {
-
- Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>(
- d3d_module.GetFunctionPointer(kCreate3D9DeviceExName));
- if (!create_func)
- return false;
-
- base::win::ScopedComPtr<IDirect3D9Ex> d3d;
- HRESULT hr = create_func(D3D_SDK_VERSION, d3d.Receive());
- if (FAILED(hr))
- return false;
-
- // Any old window will do to create the device. In practice the window to
- // present to is an argument to IDirect3DDevice9::Present.
- HWND window = GetShellWindow();
-
- D3DPRESENT_PARAMETERS parameters = { 0 };
- parameters.BackBufferWidth = 1;
- parameters.BackBufferHeight = 1;
- parameters.BackBufferCount = 1;
- parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
- parameters.hDeviceWindow = window;
- parameters.Windowed = TRUE;
- parameters.Flags = 0;
- parameters.PresentationInterval = presentation_interval;
- parameters.SwapEffect = D3DSWAPEFFECT_COPY;
-
- hr = d3d->CreateDeviceEx(
- D3DADAPTER_DEFAULT,
- device_type,
- window,
- D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING |
- D3DCREATE_DISABLE_PSGP_THREADING | D3DCREATE_MULTITHREADED,
- ¶meters,
- NULL,
- device);
- return SUCCEEDED(hr);
-}
-
-bool OpenSharedTexture(IDirect3DDevice9* device,
- int64 surface_handle,
- const gfx::Size& size,
- IDirect3DTexture9** opened_texture) {
- HANDLE handle = reinterpret_cast<HANDLE>(surface_handle);
- HRESULT hr = device->CreateTexture(size.width(),
- size.height(),
- 1,
- D3DUSAGE_RENDERTARGET,
- D3DFMT_A8R8G8B8,
- D3DPOOL_DEFAULT,
- opened_texture,
- &handle);
- return SUCCEEDED(hr);
-}
-
-bool CreateTemporaryLockableSurface(IDirect3DDevice9* device,
- const gfx::Size& size,
- IDirect3DSurface9** surface) {
- HRESULT hr = device->CreateRenderTarget(
- size.width(),
- size.height(),
- D3DFMT_A8R8G8B8,
- D3DMULTISAMPLE_NONE,
- 0,
- TRUE,
- surface,
- NULL);
- return SUCCEEDED(hr);
-}
-
-bool CreateTemporaryRenderTargetTexture(IDirect3DDevice9* device,
- const gfx::Size& size,
- IDirect3DTexture9** texture,
- IDirect3DSurface9** render_target) {
- HRESULT hr = device->CreateTexture(
- size.width(),
- size.height(),
- 1, // Levels
- D3DUSAGE_RENDERTARGET,
- D3DFMT_A8R8G8B8,
- D3DPOOL_DEFAULT,
- texture,
- NULL);
- if (!SUCCEEDED(hr))
- return false;
- hr = (*texture)->GetSurfaceLevel(0, render_target);
- return SUCCEEDED(hr);
-}
-
-} // namespace ui_surface_d3d9_utils
+// Copyright (c) 2012 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 "ui/surface/d3d9_utils_win.h" + +#include "base/file_path.h" +#include "base/scoped_native_library.h" +#include "base/win/scoped_comptr.h" +#include "ui/gfx/size.h" + +namespace { + +const wchar_t kD3D9ModuleName[] = L"d3d9.dll"; +const char kCreate3D9DeviceExName[] = "Direct3DCreate9Ex"; +typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT sdk_version, + IDirect3D9Ex **d3d); +} // namespace + +namespace ui_surface_d3d9_utils { + +bool LoadD3D9(base::ScopedNativeLibrary* storage) { + storage->Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName), NULL)); + return storage->is_valid(); +} + +bool CreateDevice(const base::ScopedNativeLibrary& d3d_module, + D3DDEVTYPE device_type, + uint32 presentation_interval, + IDirect3DDevice9Ex** device) { + + Direct3DCreate9ExFunc create_func = reinterpret_cast<Direct3DCreate9ExFunc>( + d3d_module.GetFunctionPointer(kCreate3D9DeviceExName)); + if (!create_func) + return false; + + base::win::ScopedComPtr<IDirect3D9Ex> d3d; + HRESULT hr = create_func(D3D_SDK_VERSION, d3d.Receive()); + if (FAILED(hr)) + return false; + + // Any old window will do to create the device. In practice the window to + // present to is an argument to IDirect3DDevice9::Present. + HWND window = GetShellWindow(); + + D3DPRESENT_PARAMETERS parameters = { 0 }; + parameters.BackBufferWidth = 1; + parameters.BackBufferHeight = 1; + parameters.BackBufferCount = 1; + parameters.BackBufferFormat = D3DFMT_A8R8G8B8; + parameters.hDeviceWindow = window; + parameters.Windowed = TRUE; + parameters.Flags = 0; + parameters.PresentationInterval = presentation_interval; + parameters.SwapEffect = D3DSWAPEFFECT_COPY; + + hr = d3d->CreateDeviceEx( + D3DADAPTER_DEFAULT, + device_type, + window, + D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING | + D3DCREATE_DISABLE_PSGP_THREADING | D3DCREATE_MULTITHREADED, + ¶meters, + NULL, + device); + return SUCCEEDED(hr); +} + +bool OpenSharedTexture(IDirect3DDevice9* device, + int64 surface_handle, + const gfx::Size& size, + IDirect3DTexture9** opened_texture) { + HANDLE handle = reinterpret_cast<HANDLE>(surface_handle); + HRESULT hr = device->CreateTexture(size.width(), + size.height(), + 1, + D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, + opened_texture, + &handle); + return SUCCEEDED(hr); +} + +bool CreateTemporaryLockableSurface(IDirect3DDevice9* device, + const gfx::Size& size, + IDirect3DSurface9** surface) { + HRESULT hr = device->CreateRenderTarget( + size.width(), + size.height(), + D3DFMT_A8R8G8B8, + D3DMULTISAMPLE_NONE, + 0, + TRUE, + surface, + NULL); + return SUCCEEDED(hr); +} + +bool CreateTemporaryRenderTargetTexture(IDirect3DDevice9* device, + const gfx::Size& size, + IDirect3DTexture9** texture, + IDirect3DSurface9** render_target) { + HRESULT hr = device->CreateTexture( + size.width(), + size.height(), + 1, // Levels + D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, + texture, + NULL); + if (!SUCCEEDED(hr)) + return false; + hr = (*texture)->GetSurfaceLevel(0, render_target); + return SUCCEEDED(hr); +} + +gfx::Size GetSize(IDirect3DSurface9* surface) { + D3DSURFACE_DESC surface_description; + HRESULT hr = surface->GetDesc(&surface_description); + if (FAILED(hr)) + return gfx::Size(0, 0); + return gfx::Size(surface_description.Width, surface_description.Height); +} + +gfx::Size GetSize(IDirect3DTexture9* texture) { + D3DSURFACE_DESC surface_description; + HRESULT hr = texture->GetLevelDesc(0, &surface_description); + if (FAILED(hr)) + return gfx::Size(0, 0); + return gfx::Size(surface_description.Width, surface_description.Height); +} + +} // namespace ui_surface_d3d9_utils diff --git a/ui/surface/d3d9_utils_win.h b/ui/surface/d3d9_utils_win.h index a9554dc..4f11a3a 100644 --- a/ui/surface/d3d9_utils_win.h +++ b/ui/surface/d3d9_utils_win.h @@ -1,71 +1,77 @@ -// Copyright (c) 2012 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.
-
-// Helper functions that Direct3D 9Ex code a little easier to work with for
-// the ui/surface code.
-
-#ifndef UI_SURFACE_D3D9_UTILS_WIN_H_
-#define UI_SURFACE_D3D9_UTILS_WIN_H_
-
-#include <d3d9.h>
-
-#include "base/basictypes.h"
-#include "ui/surface/surface_export.h"
-
-namespace base {
-class ScopedNativeLibrary;
-}
-
-namespace gfx {
-class Size;
-}
-
-namespace ui_surface_d3d9_utils {
-
-// Visible for testing. Loads the Direct3D9 library. Returns true on success.
-SURFACE_EXPORT
-bool LoadD3D9(base::ScopedNativeLibrary* storage);
-
-// Visible for testing. Creates a Direct3D9 device suitable for use with the
-// accelerated surface code. Returns true on success.
-SURFACE_EXPORT
-bool CreateDevice(const base::ScopedNativeLibrary& d3d_module,
- D3DDEVTYPE device_type,
- uint32 presentation_interval,
- IDirect3DDevice9Ex** device);
-
-// Calls the Vista+ (WDDM1.0) variant of CreateTexture that semantically
-// opens a texture allocated (possibly in another process) as shared. The
-// shared texture is identified by its surface handle. The resulting texture
-// is written into |opened_texture|.
-//
-// Returns true on success.
-SURFACE_EXPORT
-bool OpenSharedTexture(IDirect3DDevice9* device,
- int64 surface_handle,
- const gfx::Size& size,
- IDirect3DTexture9** opened_texture);
-
-// Create a one-off lockable surface of a specified size.
-//
-// Returns true on success.
-SURFACE_EXPORT
-bool CreateTemporaryLockableSurface(IDirect3DDevice9* device,
- const gfx::Size& size,
- IDirect3DSurface9** surface);
-
-// Create a one-off renderable texture of a specified size. The texture object
-// as well as the surface object for the texture's level 0 is returned (callers
-// almost always need to use both).
-//
-// Returns true on success.
-SURFACE_EXPORT
-bool CreateTemporaryRenderTargetTexture(IDirect3DDevice9* device,
- const gfx::Size& size,
- IDirect3DTexture9** texture,
- IDirect3DSurface9** render_target);
-
-} // namespace ui_surface_d3d9_utils
-
+// Copyright (c) 2012 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. + +// Helper functions that Direct3D 9Ex code a little easier to work with for +// the ui/surface code. + +#ifndef UI_SURFACE_D3D9_UTILS_WIN_H_ +#define UI_SURFACE_D3D9_UTILS_WIN_H_ + +#include <d3d9.h> + +#include "base/basictypes.h" +#include "ui/surface/surface_export.h" + +namespace base { +class ScopedNativeLibrary; +} + +namespace gfx { +class Size; +} + +namespace ui_surface_d3d9_utils { + +// Visible for testing. Loads the Direct3D9 library. Returns true on success. +SURFACE_EXPORT +bool LoadD3D9(base::ScopedNativeLibrary* storage); + +// Visible for testing. Creates a Direct3D9 device suitable for use with the +// accelerated surface code. Returns true on success. +SURFACE_EXPORT +bool CreateDevice(const base::ScopedNativeLibrary& d3d_module, + D3DDEVTYPE device_type, + uint32 presentation_interval, + IDirect3DDevice9Ex** device); + +// Calls the Vista+ (WDDM1.0) variant of CreateTexture that semantically +// opens a texture allocated (possibly in another process) as shared. The +// shared texture is identified by its surface handle. The resulting texture +// is written into |opened_texture|. +// +// Returns true on success. +SURFACE_EXPORT +bool OpenSharedTexture(IDirect3DDevice9* device, + int64 surface_handle, + const gfx::Size& size, + IDirect3DTexture9** opened_texture); + +// Create a one-off lockable surface of a specified size. +// +// Returns true on success. +SURFACE_EXPORT +bool CreateTemporaryLockableSurface(IDirect3DDevice9* device, + const gfx::Size& size, + IDirect3DSurface9** surface); + +// Create a one-off renderable texture of a specified size. The texture object +// as well as the surface object for the texture's level 0 is returned (callers +// almost always need to use both). +// +// Returns true on success. +SURFACE_EXPORT +bool CreateTemporaryRenderTargetTexture(IDirect3DDevice9* device, + const gfx::Size& size, + IDirect3DTexture9** texture, + IDirect3DSurface9** render_target); + +SURFACE_EXPORT +gfx::Size GetSize(IDirect3DTexture9* texture); + +SURFACE_EXPORT +gfx::Size GetSize(IDirect3DSurface9* surface); + +} // namespace ui_surface_d3d9_utils + #endif // UI_SURFACE_D3D9_UTILS_WIN_H_
\ No newline at end of file diff --git a/ui/surface/surface.gyp b/ui/surface/surface.gyp index 23cb3e8..4b4b634f 100644 --- a/ui/surface/surface.gyp +++ b/ui/surface/surface.gyp @@ -96,6 +96,8 @@ 'dependencies': [ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/base.gyp:run_all_unittests', + '<(DEPTH)/media/media.gyp:yuv_convert', + '<(DEPTH)/skia/skia.gyp:skia', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/ui/ui.gyp:ui', 'surface', |