Index: gpu/ipc/service/direct_composition_surface_win.cc |
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc |
index 381d68e412671f9d9d827e41dc34f33204ccf6da..91f77e1ad07a839b4a4e1362ed0acc395679c1f5 100644 |
--- a/gpu/ipc/service/direct_composition_surface_win.cc |
+++ b/gpu/ipc/service/direct_composition_surface_win.cc |
@@ -30,6 +30,7 @@ |
#include "ui/gl/gl_angle_util_win.h" |
#include "ui/gl/gl_context.h" |
#include "ui/gl/gl_image_dxgi.h" |
+#include "ui/gl/gl_image_memory.h" |
#include "ui/gl/gl_surface_egl.h" |
#include "ui/gl/scoped_make_current.h" |
@@ -256,6 +257,8 @@ class DCLayerTree::SwapChainPresenter { |
using PFN_DCOMPOSITION_CREATE_SURFACE_HANDLE = |
HRESULT(WINAPI*)(DWORD, SECURITY_ATTRIBUTES*, HANDLE*); |
+ bool UploadVideoImages(gl::GLImageMemory* y_image_memory, |
+ gl::GLImageMemory* uv_image_memory); |
// Returns true if the video processor changed. |
bool InitializeVideoProcessor(const gfx::Size& in_size, |
const gfx::Size& out_size); |
@@ -279,8 +282,11 @@ class DCLayerTree::SwapChainPresenter { |
bool failed_to_create_yuy2_swapchain_ = false; |
int frames_since_color_space_change_ = 0; |
- // This is the GLImage that was presented in the last frame. |
- scoped_refptr<gl::GLImageDXGI> last_gl_image_; |
+ // These are the GLImages that were presented in the last frame. |
+ std::vector<scoped_refptr<gl::GLImage>> last_gl_images_; |
+ |
+ base::win::ScopedComPtr<ID3D11Texture2D> staging_texture_; |
+ gfx::Size staging_texture_size_; |
base::win::ScopedComPtr<ID3D11Device> d3d11_device_; |
base::win::ScopedComPtr<IDXGISwapChain1> swap_chain_; |
@@ -392,11 +398,89 @@ bool DCLayerTree::SwapChainPresenter::ShouldBeYUY2() { |
} |
} |
+bool DCLayerTree::SwapChainPresenter::UploadVideoImages( |
+ gl::GLImageMemory* y_image_memory, |
+ gl::GLImageMemory* uv_image_memory) { |
+ gfx::Size texture_size = y_image_memory->GetSize(); |
+ gfx::Size uv_image_size = uv_image_memory->GetSize(); |
+ if (uv_image_size.height() != texture_size.height() / 2 || |
+ uv_image_size.width() != texture_size.width() / 2 || |
+ y_image_memory->format() != gfx::BufferFormat::R_8 || |
+ uv_image_memory->format() != gfx::BufferFormat::RG_88) { |
+ DVLOG(ERROR) << "Invalid NV12 GLImageMemory properties."; |
+ return false; |
+ } |
+ |
+ if (!staging_texture_ || (staging_texture_size_ != texture_size)) { |
+ staging_texture_.Reset(); |
+ D3D11_TEXTURE2D_DESC desc = {}; |
+ desc.Width = texture_size.width(); |
+ desc.Height = texture_size.height(); |
+ desc.Format = DXGI_FORMAT_NV12; |
+ desc.MipLevels = 1; |
+ desc.ArraySize = 1; |
+ desc.Usage = D3D11_USAGE_DYNAMIC; |
+ |
+ // This isn't actually bound to a decoder, but dynamic textures need |
+ // BindFlags to be nonzero and D3D11_BIND_DECODER also works when creating |
+ // a VideoProcessorInputView. |
+ desc.BindFlags = D3D11_BIND_DECODER; |
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; |
+ desc.MiscFlags = 0; |
+ desc.SampleDesc.Count = 1; |
+ base::win::ScopedComPtr<ID3D11Texture2D> texture; |
+ HRESULT hr = d3d11_device_->CreateTexture2D(&desc, nullptr, |
+ staging_texture_.Receive()); |
+ CHECK(SUCCEEDED(hr)) << "Creating D3D11 video upload texture failed: " |
+ << std::hex << hr; |
+ staging_texture_size_ = texture_size; |
+ } |
+ base::win::ScopedComPtr<ID3D11DeviceContext> context; |
+ d3d11_device_->GetImmediateContext(context.Receive()); |
+ D3D11_MAPPED_SUBRESOURCE mapped_resource; |
+ HRESULT hr = context->Map(staging_texture_.Get(), 0, D3D11_MAP_WRITE_DISCARD, |
+ 0, &mapped_resource); |
+ CHECK(SUCCEEDED(hr)) << "Mapping D3D11 video upload texture failed: " |
+ << std::hex << hr; |
+ |
+ size_t dest_stride = mapped_resource.RowPitch; |
+ for (int y = 0; y < texture_size.height(); y++) { |
+ const uint8_t* y_source = |
+ y_image_memory->memory() + y * y_image_memory->stride(); |
+ uint8_t* dest = |
+ reinterpret_cast<uint8_t*>(mapped_resource.pData) + dest_stride * y; |
+ memcpy(dest, y_source, texture_size.width()); |
+ } |
+ |
+ uint8_t* uv_dest_plane_start = |
+ reinterpret_cast<uint8_t*>(mapped_resource.pData) + |
+ dest_stride * texture_size.height(); |
+ for (int y = 0; y < uv_image_size.height(); y++) { |
+ const uint8_t* uv_source = |
+ uv_image_memory->memory() + y * uv_image_memory->stride(); |
+ uint8_t* dest = uv_dest_plane_start + dest_stride * y; |
+ memcpy(dest, uv_source, texture_size.width()); |
+ } |
+ context->Unmap(staging_texture_.Get(), 0); |
+ return true; |
+} |
+ |
void DCLayerTree::SwapChainPresenter::PresentToSwapChain( |
const ui::DCRendererLayerParams& params) { |
gl::GLImageDXGI* image_dxgi = |
gl::GLImageDXGI::FromGLImage(params.image[0].get()); |
- DCHECK(image_dxgi); |
+ gl::GLImageMemory* y_image_memory = nullptr; |
+ gl::GLImageMemory* uv_image_memory = nullptr; |
+ if (params.image.size() >= 2) { |
+ y_image_memory = gl::GLImageMemory::FromGLImage(params.image[0].get()); |
+ uv_image_memory = gl::GLImageMemory::FromGLImage(params.image[1].get()); |
+ } |
+ |
+ if (!image_dxgi && (!y_image_memory || !uv_image_memory)) { |
+ DLOG(ERROR) << "Video GLImages are missing"; |
+ last_gl_images_.clear(); |
+ return; |
+ } |
// Swap chain size is the minimum of the on-screen size and the source |
// size so the video processor can do the minimal amount of work and |
@@ -423,14 +507,30 @@ void DCLayerTree::SwapChainPresenter::PresentToSwapChain( |
swap_chain_size_ = swap_chain_size; |
swap_chain_.Reset(); |
ReallocateSwapChain(yuy2_swapchain); |
- } else if (last_gl_image_ == image_dxgi) { |
- // The swap chain is presenting the same image as last swap, which means |
- // that the image was never returned to the video decoder and should have |
- // the same contents as last time. It shouldn't need to be redrawn. |
+ } else if (last_gl_images_ == params.image) { |
+ // The swap chain is presenting the same images as last swap, which means |
+ // that the images were never returned to the video decoder and should |
+ // have the same contents as last time. It shouldn't need to be redrawn. |
return; |
} |
- last_gl_image_ = image_dxgi; |
+ last_gl_images_ = params.image; |
+ |
+ base::win::ScopedComPtr<ID3D11Texture2D> input_texture; |
+ UINT input_level; |
+ if (image_dxgi) { |
+ input_texture = image_dxgi->texture(); |
+ input_level = (UINT)image_dxgi->level(); |
+ staging_texture_.Reset(); |
+ } else { |
+ DCHECK(y_image_memory); |
+ DCHECK(uv_image_memory); |
+ if (!UploadVideoImages(y_image_memory, uv_image_memory)) |
+ return; |
+ DCHECK(staging_texture_); |
+ input_texture = staging_texture_; |
+ input_level = 0; |
+ } |
if (!out_view_) { |
base::win::ScopedComPtr<ID3D11Texture2D> texture; |
@@ -487,11 +587,11 @@ void DCLayerTree::SwapChainPresenter::PresentToSwapChain( |
{ |
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC in_desc = {}; |
in_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; |
- in_desc.Texture2D.ArraySlice = (UINT)image_dxgi->level(); |
+ in_desc.Texture2D.ArraySlice = input_level; |
base::win::ScopedComPtr<ID3D11VideoProcessorInputView> in_view; |
HRESULT hr = video_device_->CreateVideoProcessorInputView( |
- image_dxgi->texture().Get(), video_processor_enumerator_.Get(), |
- &in_desc, in_view.Receive()); |
+ input_texture.Get(), video_processor_enumerator_.Get(), &in_desc, |
+ in_view.Receive()); |
CHECK(SUCCEEDED(hr)); |
D3D11_VIDEO_PROCESSOR_STREAM stream = {}; |
@@ -809,8 +909,7 @@ bool DCLayerTree::CommitAndClearPendingOverlays() { |
VisualInfo* visual_info = &visual_info_[i]; |
InitVisual(i); |
- if (params.image.size() > 0 && params.image[0] && |
- params.image[0]->GetType() == gl::GLImage::Type::DXGI_IMAGE) { |
+ if (params.image.size() >= 1 && params.image[0]) { |
UpdateVisualForVideo(visual_info, params); |
} else if (params.image.empty()) { |
UpdateVisualForBackbuffer(visual_info, params); |