Index: ui/surface/accelerated_surface_transformer_win.cc |
diff --git a/ui/surface/accelerated_surface_transformer_win.cc b/ui/surface/accelerated_surface_transformer_win.cc |
index cfc031b020cf4c693d74e0cde8d830cc6d5eab7a..694f1695faceb753b808b1fe36ff1dbb515125f5 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; |
-} |
+// 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; |
+} |