summaryrefslogtreecommitdiffstats
path: root/ui/gfx/surface
diff options
context:
space:
mode:
authormazda@chromium.org <mazda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-15 09:29:36 +0000
committermazda@chromium.org <mazda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-15 09:29:36 +0000
commitca2fd075cf4eaeb2ee650affff606bbb3120772c (patch)
tree4cb40434aa433d9e30bba39e103b4ec8b59c4f59 /ui/gfx/surface
parent0c652b8e854aad1f44751449e7029f432f4502fe (diff)
downloadchromium_src-ca2fd075cf4eaeb2ee650affff606bbb3120772c.zip
chromium_src-ca2fd075cf4eaeb2ee650affff606bbb3120772c.tar.gz
chromium_src-ca2fd075cf4eaeb2ee650affff606bbb3120772c.tar.bz2
Support browser side thumbnailing for GPU-composited pages on Windows.
Benchmark results of copying a 1280 x 800 pixels using CopyFromCompositingSurface or CopyFromBackingSurface are as follows. FromCompositingSurface: 40.00 ms FromBackingStore: 2.12 ms I'm prepareing the following CLs and these will come soon, - Support Mac and Chrome OS (Aura) - Reduce the number of pixels to copy from the compositing surface for performance improvement BUG=96351 TEST=Manual Review URL: http://codereview.chromium.org/9582003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126870 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/gfx/surface')
-rw-r--r--ui/gfx/surface/accelerated_surface_win.cc147
-rw-r--r--ui/gfx/surface/accelerated_surface_win.h7
2 files changed, 153 insertions, 1 deletions
diff --git a/ui/gfx/surface/accelerated_surface_win.cc b/ui/gfx/surface/accelerated_surface_win.cc
index adff364..bf24711 100644
--- a/ui/gfx/surface/accelerated_surface_win.cc
+++ b/ui/gfx/surface/accelerated_surface_win.cc
@@ -5,6 +5,7 @@
#include "ui/gfx/surface/accelerated_surface_win.h"
#include <windows.h>
+#include <algorithm>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -38,6 +39,47 @@ UINT GetPresentationInterval() {
return D3DPRESENT_INTERVAL_ONE;
}
+// Calculate the number necessary to transform |source_size| into |dest_size|
+// by repeating downsampling of the image of |source_size| by a factor no more
+// than 2.
+int GetResampleCount(const gfx::Size& source_size, const gfx::Size& dest_size) {
+ int width_count = 0;
+ int width = source_size.width();
+ while (width > dest_size.width()) {
+ ++width_count;
+ width >>= 1;
+ }
+ int height_count = 0;
+ int height = source_size.height();
+ while (height > dest_size.height()) {
+ ++height_count;
+ height >>= 1;
+ }
+ return std::max(width_count, height_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));
+}
+
+bool CreateTemporarySurface(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);
+}
+
} // namespace anonymous
// A PresentThread is a thread that is dedicated to presenting surfaces to a
@@ -240,6 +282,106 @@ bool AcceleratedPresenter::Present(gfx::NativeWindow window) {
return true;
}
+bool AcceleratedPresenter::CopyTo(const gfx::Size& size, void* buf) {
+ base::AutoLock locked(lock_);
+
+ if (!swap_chain_)
+ return false;
+
+ base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
+ HRESULT hr = swap_chain_->GetBackBuffer(0,
+ D3DBACKBUFFER_TYPE_MONO,
+ back_buffer.Receive());
+ if (FAILED(hr))
+ return false;
+
+ D3DSURFACE_DESC desc;
+ hr = back_buffer->GetDesc(&desc);
+ if (FAILED(hr))
+ return false;
+
+ const gfx::Size back_buffer_size(desc.Width, desc.Height);
+ if (back_buffer_size.IsEmpty())
+ return false;
+
+ // Set up intermediate buffers needed for downsampling.
+ const int resample_count =
+ GetResampleCount(gfx::Size(desc.Width, desc.Height), size);
+ base::win::ScopedComPtr<IDirect3DSurface9> final_surface;
+ base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2];
+ if (resample_count == 0)
+ final_surface = back_buffer;
+ if (resample_count > 0) {
+ if (!CreateTemporarySurface(present_thread_->device(),
+ size,
+ final_surface.Receive()))
+ return false;
+ }
+ const gfx::Size half_size = GetHalfSizeNoLessThan(back_buffer_size, size);
+ if (resample_count > 1) {
+ if (!CreateTemporarySurface(present_thread_->device(),
+ half_size,
+ temp_buffer[0].Receive()))
+ return false;
+ }
+ if (resample_count > 2) {
+ const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, size);
+ if (!CreateTemporarySurface(present_thread_->device(),
+ quarter_size,
+ temp_buffer[1].Receive()))
+ return false;
+ }
+
+ // Repeat downsampling the surface until its size becomes identical to
+ // |size|. We keep the factor of each downsampling no more than two because
+ // using a factor more than two can introduce aliasing.
+ gfx::Size read_size = back_buffer_size;
+ gfx::Size write_size = half_size;
+ int read_buffer_index = 1;
+ int write_buffer_index = 0;
+ for (int i = 0; i < resample_count; ++i) {
+ base::win::ScopedComPtr<IDirect3DSurface9> read_buffer =
+ (i == 0) ? back_buffer : temp_buffer[read_buffer_index];
+ base::win::ScopedComPtr<IDirect3DSurface9> write_buffer =
+ (i == resample_count - 1) ? final_surface :
+ temp_buffer[write_buffer_index];
+ RECT read_rect = {0, 0, read_size.width(), read_size.height()};
+ RECT write_rect = {0, 0, write_size.width(), write_size.height()};
+ hr = present_thread_->device()->StretchRect(read_buffer,
+ &read_rect,
+ write_buffer,
+ &write_rect,
+ D3DTEXF_LINEAR);
+ if (FAILED(hr))
+ return false;
+ read_size = write_size;
+ write_size = GetHalfSizeNoLessThan(write_size, size);
+ std::swap(read_buffer_index, write_buffer_index);
+ }
+
+ DCHECK(size == read_size);
+
+ base::win::ScopedComPtr<IDirect3DSurface9> temp_surface;
+ HANDLE handle = reinterpret_cast<HANDLE>(buf);
+ hr = present_thread_->device()->CreateOffscreenPlainSurface(
+ size.width(),
+ size.height(),
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ temp_surface.Receive(),
+ &handle);
+ if (FAILED(hr))
+ return false;
+
+ // Copy the data in the temporary buffer to the surface backed by |buf|.
+ hr = present_thread_->device()->GetRenderTargetData(final_surface,
+ temp_surface);
+ if (FAILED(hr))
+ return false;
+
+ return true;
+}
+
void AcceleratedPresenter::Suspend() {
present_thread_->message_loop()->PostTask(
FROM_HERE,
@@ -444,7 +586,10 @@ bool AcceleratedSurface::Present(HWND window) {
return presenter_->Present(window);
}
+bool AcceleratedSurface::CopyTo(const gfx::Size& size, void* buf) {
+ return presenter_->CopyTo(size, buf);
+}
+
void AcceleratedSurface::Suspend() {
presenter_->Suspend();
}
-
diff --git a/ui/gfx/surface/accelerated_surface_win.h b/ui/gfx/surface/accelerated_surface_win.h
index 9b48b36..52208d3 100644
--- a/ui/gfx/surface/accelerated_surface_win.h
+++ b/ui/gfx/surface/accelerated_surface_win.h
@@ -32,6 +32,7 @@ class AcceleratedPresenter
int64 surface_id,
const base::Callback<void(bool)>& completion_task);
bool Present(gfx::NativeWindow window);
+ bool CopyTo(const gfx::Size& size, void* buf);
void Suspend();
void WaitForPendingTasks();
@@ -85,6 +86,12 @@ class SURFACE_EXPORT AcceleratedSurface {
// Synchronously present a frame with no acknowledgement.
bool Present(gfx::NativeWindow window);
+ // Copies the surface data to |buf|. The image data is transformed so that it
+ // fits in |size|.
+ // Caller must ensure that |buf| is allocated with the size no less than
+ // |4 * size.width() * size.height()| bytes.
+ bool CopyTo(const gfx::Size& size, void* buf);
+
// Temporarily release resources until a new surface is asynchronously
// presented. Present will not be able to represent the last surface after
// calling this and will return false.