OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/gpu/media/dxva_video_decode_accelerator_win.h" | 5 #include "content/common/gpu/media/dxva_video_decode_accelerator_win.h" |
6 | 6 |
7 #if !defined(OS_WIN) | 7 #if !defined(OS_WIN) |
8 #error This file should only be built on Windows. | 8 #error This file should only be built on Windows. |
9 #endif // !defined(OS_WIN) | 9 #endif // !defined(OS_WIN) |
10 | 10 |
(...skipping 23 matching lines...) Expand all Loading... |
34 #include "base/trace_event/trace_event.h" | 34 #include "base/trace_event/trace_event.h" |
35 #include "base/win/windows_version.h" | 35 #include "base/win/windows_version.h" |
36 #include "build/build_config.h" | 36 #include "build/build_config.h" |
37 #include "content/public/common/content_switches.h" | 37 #include "content/public/common/content_switches.h" |
38 #include "media/base/win/mf_initializer.h" | 38 #include "media/base/win/mf_initializer.h" |
39 #include "media/video/video_decode_accelerator.h" | 39 #include "media/video/video_decode_accelerator.h" |
40 #include "third_party/angle/include/EGL/egl.h" | 40 #include "third_party/angle/include/EGL/egl.h" |
41 #include "third_party/angle/include/EGL/eglext.h" | 41 #include "third_party/angle/include/EGL/eglext.h" |
42 #include "ui/gl/gl_bindings.h" | 42 #include "ui/gl/gl_bindings.h" |
43 #include "ui/gl/gl_context.h" | 43 #include "ui/gl/gl_context.h" |
| 44 #include "ui/gl/gl_fence.h" |
44 #include "ui/gl/gl_surface_egl.h" | 45 #include "ui/gl/gl_surface_egl.h" |
45 #include "ui/gl/gl_switches.h" | 46 #include "ui/gl/gl_switches.h" |
46 | 47 |
47 namespace { | 48 namespace { |
48 | 49 |
49 // Path is appended on to the PROGRAM_FILES base path. | 50 // Path is appended on to the PROGRAM_FILES base path. |
50 const wchar_t kVPXDecoderDLLPath[] = L"Intel\\Media SDK\\"; | 51 const wchar_t kVPXDecoderDLLPath[] = L"Intel\\Media SDK\\"; |
51 | 52 |
52 const wchar_t kVP8DecoderDLLName[] = | 53 const wchar_t kVP8DecoderDLLName[] = |
53 #if defined(ARCH_CPU_X86) | 54 #if defined(ARCH_CPU_X86) |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 log << ", HRESULT: 0x" << std::hex << result, \ | 156 log << ", HRESULT: 0x" << std::hex << result, \ |
156 error_code, ret); | 157 error_code, ret); |
157 | 158 |
158 enum { | 159 enum { |
159 // Maximum number of iterations we allow before aborting the attempt to flush | 160 // Maximum number of iterations we allow before aborting the attempt to flush |
160 // the batched queries to the driver and allow torn/corrupt frames to be | 161 // the batched queries to the driver and allow torn/corrupt frames to be |
161 // rendered. | 162 // rendered. |
162 kFlushDecoderSurfaceTimeoutMs = 1, | 163 kFlushDecoderSurfaceTimeoutMs = 1, |
163 // Maximum iterations where we try to flush the d3d device. | 164 // Maximum iterations where we try to flush the d3d device. |
164 kMaxIterationsForD3DFlush = 4, | 165 kMaxIterationsForD3DFlush = 4, |
| 166 // Maximum iterations where we try to flush the ANGLE device before reusing |
| 167 // the texture. |
| 168 kMaxIterationsForANGLEReuseFlush = 16, |
165 // We only request 5 picture buffers from the client which are used to hold | 169 // We only request 5 picture buffers from the client which are used to hold |
166 // the decoded samples. These buffers are then reused when the client tells | 170 // the decoded samples. These buffers are then reused when the client tells |
167 // us that it is done with the buffer. | 171 // us that it is done with the buffer. |
168 kNumPictureBuffers = 5, | 172 kNumPictureBuffers = 5, |
169 // The keyed mutex should always be released before the other thread | 173 // The keyed mutex should always be released before the other thread |
170 // attempts to acquire it, so AcquireSync should always return immediately. | 174 // attempts to acquire it, so AcquireSync should always return immediately. |
171 kAcquireSyncWaitMs = 0, | 175 kAcquireSyncWaitMs = 0, |
172 }; | 176 }; |
173 | 177 |
174 static IMFSample* CreateEmptySample() { | 178 static IMFSample* CreateEmptySample() { |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 static linked_ptr<DXVAPictureBuffer> Create( | 342 static linked_ptr<DXVAPictureBuffer> Create( |
339 const DXVAVideoDecodeAccelerator& decoder, | 343 const DXVAVideoDecodeAccelerator& decoder, |
340 const media::PictureBuffer& buffer, | 344 const media::PictureBuffer& buffer, |
341 EGLConfig egl_config); | 345 EGLConfig egl_config); |
342 ~DXVAPictureBuffer(); | 346 ~DXVAPictureBuffer(); |
343 | 347 |
344 bool InitializeTexture(const DXVAVideoDecodeAccelerator& decoder, | 348 bool InitializeTexture(const DXVAVideoDecodeAccelerator& decoder, |
345 bool use_rgb); | 349 bool use_rgb); |
346 | 350 |
347 bool ReusePictureBuffer(); | 351 bool ReusePictureBuffer(); |
| 352 void ResetReuseFence(); |
348 // Copies the output sample data to the picture buffer provided by the | 353 // Copies the output sample data to the picture buffer provided by the |
349 // client. | 354 // client. |
350 // The dest_surface parameter contains the decoded bits. | 355 // The dest_surface parameter contains the decoded bits. |
351 bool CopyOutputSampleDataToPictureBuffer( | 356 bool CopyOutputSampleDataToPictureBuffer( |
352 DXVAVideoDecodeAccelerator* decoder, | 357 DXVAVideoDecodeAccelerator* decoder, |
353 IDirect3DSurface9* dest_surface, | 358 IDirect3DSurface9* dest_surface, |
354 ID3D11Texture2D* dx11_texture, | 359 ID3D11Texture2D* dx11_texture, |
355 int input_buffer_id); | 360 int input_buffer_id); |
356 | 361 |
357 bool available() const { | 362 bool available() const { |
358 return available_; | 363 return available_; |
359 } | 364 } |
360 | 365 |
361 void set_available(bool available) { | 366 void set_available(bool available) { |
362 available_ = available; | 367 available_ = available; |
363 } | 368 } |
364 | 369 |
365 int id() const { | 370 int id() const { |
366 return picture_buffer_.id(); | 371 return picture_buffer_.id(); |
367 } | 372 } |
368 | 373 |
369 gfx::Size size() const { | 374 gfx::Size size() const { |
370 return picture_buffer_.size(); | 375 return picture_buffer_.size(); |
371 } | 376 } |
372 | 377 |
| 378 bool waiting_to_reuse() const { return waiting_to_reuse_; } |
| 379 |
| 380 gfx::GLFence* reuse_fence() { return reuse_fence_.get(); } |
| 381 |
373 // Called when the source surface |src_surface| is copied to the destination | 382 // Called when the source surface |src_surface| is copied to the destination |
374 // |dest_surface| | 383 // |dest_surface| |
375 bool CopySurfaceComplete(IDirect3DSurface9* src_surface, | 384 bool CopySurfaceComplete(IDirect3DSurface9* src_surface, |
376 IDirect3DSurface9* dest_surface); | 385 IDirect3DSurface9* dest_surface); |
377 | 386 |
378 private: | 387 private: |
379 explicit DXVAPictureBuffer(const media::PictureBuffer& buffer); | 388 explicit DXVAPictureBuffer(const media::PictureBuffer& buffer); |
380 | 389 |
381 bool available_; | 390 bool available_; |
| 391 |
| 392 // This is true if the decoder is currently waiting on the fence before |
| 393 // reusing the buffer. |
| 394 bool waiting_to_reuse_; |
382 media::PictureBuffer picture_buffer_; | 395 media::PictureBuffer picture_buffer_; |
383 EGLSurface decoding_surface_; | 396 EGLSurface decoding_surface_; |
| 397 scoped_ptr<gfx::GLFence> reuse_fence_; |
384 | 398 |
385 HANDLE texture_share_handle_; | 399 HANDLE texture_share_handle_; |
386 base::win::ScopedComPtr<IDirect3DTexture9> decoding_texture_; | 400 base::win::ScopedComPtr<IDirect3DTexture9> decoding_texture_; |
387 base::win::ScopedComPtr<ID3D11Texture2D> dx11_decoding_texture_; | 401 base::win::ScopedComPtr<ID3D11Texture2D> dx11_decoding_texture_; |
388 | 402 |
389 base::win::ScopedComPtr<IDXGIKeyedMutex> egl_keyed_mutex_; | 403 base::win::ScopedComPtr<IDXGIKeyedMutex> egl_keyed_mutex_; |
390 base::win::ScopedComPtr<IDXGIKeyedMutex> dx11_keyed_mutex_; | 404 base::win::ScopedComPtr<IDXGIKeyedMutex> dx11_keyed_mutex_; |
391 | 405 |
392 // This is the last value that was used to release the keyed mutex. | 406 // This is the last value that was used to release the keyed mutex. |
393 uint64_t keyed_mutex_value_; | 407 uint64_t keyed_mutex_value_; |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
501 RETURN_ON_HR_FAILURE(hr, "Failed to create texture", false); | 515 RETURN_ON_HR_FAILURE(hr, "Failed to create texture", false); |
502 RETURN_ON_FAILURE(texture_share_handle_, "Failed to query shared handle", | 516 RETURN_ON_FAILURE(texture_share_handle_, "Failed to query shared handle", |
503 false); | 517 false); |
504 } | 518 } |
505 return true; | 519 return true; |
506 } | 520 } |
507 | 521 |
508 DXVAVideoDecodeAccelerator::DXVAPictureBuffer::DXVAPictureBuffer( | 522 DXVAVideoDecodeAccelerator::DXVAPictureBuffer::DXVAPictureBuffer( |
509 const media::PictureBuffer& buffer) | 523 const media::PictureBuffer& buffer) |
510 : available_(true), | 524 : available_(true), |
| 525 waiting_to_reuse_(false), |
511 picture_buffer_(buffer), | 526 picture_buffer_(buffer), |
512 decoding_surface_(NULL), | 527 decoding_surface_(NULL), |
513 texture_share_handle_(nullptr), | 528 texture_share_handle_(nullptr), |
514 keyed_mutex_value_(0), | 529 keyed_mutex_value_(0), |
515 use_rgb_(true) {} | 530 use_rgb_(true) {} |
516 | 531 |
517 DXVAVideoDecodeAccelerator::DXVAPictureBuffer::~DXVAPictureBuffer() { | 532 DXVAVideoDecodeAccelerator::DXVAPictureBuffer::~DXVAPictureBuffer() { |
518 if (decoding_surface_) { | 533 if (decoding_surface_) { |
519 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); | 534 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); |
520 | 535 |
(...skipping 12 matching lines...) Expand all Loading... |
533 bool DXVAVideoDecodeAccelerator::DXVAPictureBuffer::ReusePictureBuffer() { | 548 bool DXVAVideoDecodeAccelerator::DXVAPictureBuffer::ReusePictureBuffer() { |
534 DCHECK(decoding_surface_); | 549 DCHECK(decoding_surface_); |
535 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); | 550 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); |
536 eglReleaseTexImage( | 551 eglReleaseTexImage( |
537 egl_display, | 552 egl_display, |
538 decoding_surface_, | 553 decoding_surface_, |
539 EGL_BACK_BUFFER); | 554 EGL_BACK_BUFFER); |
540 decoder_surface_.Release(); | 555 decoder_surface_.Release(); |
541 target_surface_.Release(); | 556 target_surface_.Release(); |
542 decoder_dx11_texture_.Release(); | 557 decoder_dx11_texture_.Release(); |
| 558 waiting_to_reuse_ = false; |
543 set_available(true); | 559 set_available(true); |
544 if (egl_keyed_mutex_) { | 560 if (egl_keyed_mutex_) { |
545 HRESULT hr = egl_keyed_mutex_->ReleaseSync(++keyed_mutex_value_); | 561 HRESULT hr = egl_keyed_mutex_->ReleaseSync(++keyed_mutex_value_); |
546 RETURN_ON_FAILURE(hr == S_OK, "Could not release sync mutex", false); | 562 RETURN_ON_FAILURE(hr == S_OK, "Could not release sync mutex", false); |
547 } | 563 } |
548 return true; | 564 return true; |
549 } | 565 } |
550 | 566 |
| 567 void DXVAVideoDecodeAccelerator::DXVAPictureBuffer::ResetReuseFence() { |
| 568 if (!reuse_fence_ || !reuse_fence_->ResetSupported()) |
| 569 reuse_fence_.reset(gfx::GLFence::Create()); |
| 570 else |
| 571 reuse_fence_->ResetState(); |
| 572 waiting_to_reuse_ = true; |
| 573 } |
| 574 |
551 bool DXVAVideoDecodeAccelerator::DXVAPictureBuffer:: | 575 bool DXVAVideoDecodeAccelerator::DXVAPictureBuffer:: |
552 CopyOutputSampleDataToPictureBuffer( | 576 CopyOutputSampleDataToPictureBuffer( |
553 DXVAVideoDecodeAccelerator* decoder, | 577 DXVAVideoDecodeAccelerator* decoder, |
554 IDirect3DSurface9* dest_surface, | 578 IDirect3DSurface9* dest_surface, |
555 ID3D11Texture2D* dx11_texture, | 579 ID3D11Texture2D* dx11_texture, |
556 int input_buffer_id) { | 580 int input_buffer_id) { |
557 DCHECK(dest_surface || dx11_texture); | 581 DCHECK(dest_surface || dx11_texture); |
558 if (dx11_texture) { | 582 if (dx11_texture) { |
559 // Grab a reference on the decoder texture. This reference will be released | 583 // Grab a reference on the decoder texture. This reference will be released |
560 // when we receive a notification that the copy was completed or when the | 584 // when we receive a notification that the copy was completed or when the |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
732 create_dxgi_device_manager_ = reinterpret_cast<CreateDXGIDeviceManager>( | 756 create_dxgi_device_manager_ = reinterpret_cast<CreateDXGIDeviceManager>( |
733 ::GetProcAddress(dxgi_manager_dll, "MFCreateDXGIDeviceManager")); | 757 ::GetProcAddress(dxgi_manager_dll, "MFCreateDXGIDeviceManager")); |
734 } | 758 } |
735 | 759 |
736 RETURN_AND_NOTIFY_ON_FAILURE( | 760 RETURN_AND_NOTIFY_ON_FAILURE( |
737 gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle, | 761 gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle, |
738 "EGL_ANGLE_surface_d3d_texture_2d_share_handle unavailable", | 762 "EGL_ANGLE_surface_d3d_texture_2d_share_handle unavailable", |
739 PLATFORM_FAILURE, | 763 PLATFORM_FAILURE, |
740 false); | 764 false); |
741 | 765 |
| 766 RETURN_AND_NOTIFY_ON_FAILURE(gfx::GLFence::IsSupported(), |
| 767 "GL fences are unsupported", PLATFORM_FAILURE, |
| 768 false); |
| 769 |
742 State state = GetState(); | 770 State state = GetState(); |
743 RETURN_AND_NOTIFY_ON_FAILURE((state == kUninitialized), | 771 RETURN_AND_NOTIFY_ON_FAILURE((state == kUninitialized), |
744 "Initialize: invalid state: " << state, ILLEGAL_STATE, false); | 772 "Initialize: invalid state: " << state, ILLEGAL_STATE, false); |
745 | 773 |
746 media::InitializeMediaFoundation(); | 774 media::InitializeMediaFoundation(); |
747 | 775 |
748 RETURN_AND_NOTIFY_ON_FAILURE(InitDecoder(config.profile), | 776 RETURN_AND_NOTIFY_ON_FAILURE(InitDecoder(config.profile), |
749 "Failed to initialize decoder", PLATFORM_FAILURE, false); | 777 "Failed to initialize decoder", PLATFORM_FAILURE, false); |
750 | 778 |
751 RETURN_AND_NOTIFY_ON_FAILURE(GetStreamsInfoAndBufferReqs(), | 779 RETURN_AND_NOTIFY_ON_FAILURE(GetStreamsInfoAndBufferReqs(), |
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1016 it = stale_output_picture_buffers_.find(picture_buffer_id); | 1044 it = stale_output_picture_buffers_.find(picture_buffer_id); |
1017 RETURN_AND_NOTIFY_ON_FAILURE(it != stale_output_picture_buffers_.end(), | 1045 RETURN_AND_NOTIFY_ON_FAILURE(it != stale_output_picture_buffers_.end(), |
1018 "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,); | 1046 "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,); |
1019 main_thread_task_runner_->PostTask( | 1047 main_thread_task_runner_->PostTask( |
1020 FROM_HERE, | 1048 FROM_HERE, |
1021 base::Bind(&DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer, | 1049 base::Bind(&DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer, |
1022 weak_this_factory_.GetWeakPtr(), picture_buffer_id)); | 1050 weak_this_factory_.GetWeakPtr(), picture_buffer_id)); |
1023 return; | 1051 return; |
1024 } | 1052 } |
1025 | 1053 |
1026 RETURN_AND_NOTIFY_ON_FAILURE(it->second->ReusePictureBuffer(), | 1054 if (it->second->available() || it->second->waiting_to_reuse()) |
| 1055 return; |
| 1056 |
| 1057 if (use_keyed_mutex_ || using_angle_device_) { |
| 1058 RETURN_AND_NOTIFY_ON_FAILURE(it->second->ReusePictureBuffer(), |
| 1059 "Failed to reuse picture buffer", |
| 1060 PLATFORM_FAILURE, ); |
| 1061 |
| 1062 ProcessPendingSamples(); |
| 1063 if (pending_flush_) { |
| 1064 decoder_thread_task_runner_->PostTask( |
| 1065 FROM_HERE, base::Bind(&DXVAVideoDecodeAccelerator::FlushInternal, |
| 1066 base::Unretained(this))); |
| 1067 } |
| 1068 } else { |
| 1069 RETURN_AND_NOTIFY_ON_FAILURE(make_context_current_.Run(), |
| 1070 "Failed to make context current", |
| 1071 PLATFORM_FAILURE, ); |
| 1072 it->second->ResetReuseFence(); |
| 1073 |
| 1074 WaitForOutputBuffer(picture_buffer_id, 0); |
| 1075 } |
| 1076 } |
| 1077 |
| 1078 void DXVAVideoDecodeAccelerator::WaitForOutputBuffer(int32_t picture_buffer_id, |
| 1079 int count) { |
| 1080 DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); |
| 1081 OutputBuffers::iterator it = output_picture_buffers_.find(picture_buffer_id); |
| 1082 if (it == output_picture_buffers_.end()) |
| 1083 return; |
| 1084 |
| 1085 DXVAPictureBuffer* picture_buffer = it->second.get(); |
| 1086 |
| 1087 DCHECK(!picture_buffer->available()); |
| 1088 DCHECK(picture_buffer->waiting_to_reuse()); |
| 1089 |
| 1090 gfx::GLFence* fence = picture_buffer->reuse_fence(); |
| 1091 RETURN_AND_NOTIFY_ON_FAILURE(make_context_current_.Run(), |
| 1092 "Failed to make context current", |
| 1093 PLATFORM_FAILURE, ); |
| 1094 if (count <= kMaxIterationsForANGLEReuseFlush && !fence->HasCompleted()) { |
| 1095 main_thread_task_runner_->PostDelayedTask( |
| 1096 FROM_HERE, base::Bind(&DXVAVideoDecodeAccelerator::WaitForOutputBuffer, |
| 1097 weak_this_factory_.GetWeakPtr(), |
| 1098 picture_buffer_id, count + 1), |
| 1099 base::TimeDelta::FromMilliseconds(kFlushDecoderSurfaceTimeoutMs)); |
| 1100 return; |
| 1101 } |
| 1102 RETURN_AND_NOTIFY_ON_FAILURE(picture_buffer->ReusePictureBuffer(), |
1027 "Failed to reuse picture buffer", | 1103 "Failed to reuse picture buffer", |
1028 PLATFORM_FAILURE, ); | 1104 PLATFORM_FAILURE, ); |
1029 | 1105 |
1030 ProcessPendingSamples(); | 1106 ProcessPendingSamples(); |
1031 if (pending_flush_) { | 1107 if (pending_flush_) { |
1032 decoder_thread_task_runner_->PostTask( | 1108 decoder_thread_task_runner_->PostTask( |
1033 FROM_HERE, | 1109 FROM_HERE, |
1034 base::Bind(&DXVAVideoDecodeAccelerator::FlushInternal, | 1110 base::Bind(&DXVAVideoDecodeAccelerator::FlushInternal, |
1035 base::Unretained(this))); | 1111 base::Unretained(this))); |
1036 } | 1112 } |
(...skipping 1343 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2380 } | 2456 } |
2381 RETURN_ON_HR_FAILURE(hr, "Failed to set output type", false); | 2457 RETURN_ON_HR_FAILURE(hr, "Failed to set output type", false); |
2382 return true; | 2458 return true; |
2383 } | 2459 } |
2384 media_type.Release(); | 2460 media_type.Release(); |
2385 } | 2461 } |
2386 return false; | 2462 return false; |
2387 } | 2463 } |
2388 | 2464 |
2389 } // namespace content | 2465 } // namespace content |
OLD | NEW |