| 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.h" | 5 #include "content/common/gpu/media/dxva_video_decode_accelerator.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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 StopOnError(error_code); \ | 66 StopOnError(error_code); \ |
| 67 return ret; \ | 67 return ret; \ |
| 68 } \ | 68 } \ |
| 69 } while (0) | 69 } while (0) |
| 70 | 70 |
| 71 #define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \ | 71 #define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \ |
| 72 RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \ | 72 RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \ |
| 73 log << ", HRESULT: 0x" << std::hex << result, \ | 73 log << ", HRESULT: 0x" << std::hex << result, \ |
| 74 error_code, ret); | 74 error_code, ret); |
| 75 | 75 |
| 76 // Maximum number of iterations we allow before aborting the attempt to flush |
| 77 // the batched queries to the driver and allow torn/corrupt frames to be |
| 78 // rendered. |
| 79 enum { kMaxIterationsForD3DFlush = 10 }; |
| 80 |
| 76 static IMFSample* CreateEmptySample() { | 81 static IMFSample* CreateEmptySample() { |
| 77 base::win::ScopedComPtr<IMFSample> sample; | 82 base::win::ScopedComPtr<IMFSample> sample; |
| 78 HRESULT hr = MFCreateSample(sample.Receive()); | 83 HRESULT hr = MFCreateSample(sample.Receive()); |
| 79 RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", NULL); | 84 RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", NULL); |
| 80 return sample.Detach(); | 85 return sample.Detach(); |
| 81 } | 86 } |
| 82 | 87 |
| 83 // Creates a Media Foundation sample with one buffer of length |buffer_length| | 88 // Creates a Media Foundation sample with one buffer of length |buffer_length| |
| 84 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. | 89 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. |
| 85 static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { | 90 static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 305 | 310 |
| 306 HRESULT hr = DXVAVideoDecodeAccelerator::device_->CreateTexture( | 311 HRESULT hr = DXVAVideoDecodeAccelerator::device_->CreateTexture( |
| 307 buffer.size().width(), | 312 buffer.size().width(), |
| 308 buffer.size().height(), | 313 buffer.size().height(), |
| 309 1, | 314 1, |
| 310 D3DUSAGE_RENDERTARGET, | 315 D3DUSAGE_RENDERTARGET, |
| 311 D3DFMT_X8R8G8B8, | 316 D3DFMT_X8R8G8B8, |
| 312 D3DPOOL_DEFAULT, | 317 D3DPOOL_DEFAULT, |
| 313 picture_buffer->decoding_texture_.Receive(), | 318 picture_buffer->decoding_texture_.Receive(), |
| 314 &share_handle); | 319 &share_handle); |
| 320 |
| 315 RETURN_ON_HR_FAILURE(hr, "Failed to create texture", | 321 RETURN_ON_HR_FAILURE(hr, "Failed to create texture", |
| 316 linked_ptr<DXVAPictureBuffer>(NULL)); | 322 linked_ptr<DXVAPictureBuffer>(NULL)); |
| 317 return picture_buffer; | 323 return picture_buffer; |
| 318 } | 324 } |
| 319 | 325 |
| 320 DXVAVideoDecodeAccelerator::DXVAPictureBuffer::DXVAPictureBuffer( | 326 DXVAVideoDecodeAccelerator::DXVAPictureBuffer::DXVAPictureBuffer( |
| 321 const media::PictureBuffer& buffer) | 327 const media::PictureBuffer& buffer) |
| 322 : available_(true), | 328 : available_(true), |
| 323 picture_buffer_(buffer), | 329 picture_buffer_(buffer), |
| 324 decoding_surface_(NULL) { | 330 decoding_surface_(NULL) { |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 HRESULT hr = decoding_texture_->GetSurfaceLevel(0, d3d_surface.Receive()); | 391 HRESULT hr = decoding_texture_->GetSurfaceLevel(0, d3d_surface.Receive()); |
| 386 RETURN_ON_HR_FAILURE(hr, "Failed to get surface from texture", false); | 392 RETURN_ON_HR_FAILURE(hr, "Failed to get surface from texture", false); |
| 387 | 393 |
| 388 hr = device_->StretchRect(dest_surface, | 394 hr = device_->StretchRect(dest_surface, |
| 389 NULL, | 395 NULL, |
| 390 d3d_surface, | 396 d3d_surface, |
| 391 NULL, | 397 NULL, |
| 392 D3DTEXF_NONE); | 398 D3DTEXF_NONE); |
| 393 RETURN_ON_HR_FAILURE(hr, "Colorspace conversion via StretchRect failed", | 399 RETURN_ON_HR_FAILURE(hr, "Colorspace conversion via StretchRect failed", |
| 394 false); | 400 false); |
| 401 |
| 395 // Ideally, this should be done immediately before the draw call that uses | 402 // Ideally, this should be done immediately before the draw call that uses |
| 396 // the texture. Flush it once here though. | 403 // the texture. Flush it once here though. |
| 397 hr = query_->Issue(D3DISSUE_END); | 404 hr = query_->Issue(D3DISSUE_END); |
| 398 RETURN_ON_HR_FAILURE(hr, "Failed to issue END", false); | 405 RETURN_ON_HR_FAILURE(hr, "Failed to issue END", false); |
| 399 do { | 406 |
| 400 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); | 407 // The DXVA decoder has its own device which it uses for decoding. ANGLE |
| 401 if (hr == S_FALSE) | 408 // has its own device which we don't have access to. |
| 402 Sleep(1); // Poor-man's Yield(). | 409 // The above code attempts to copy the decoded picture into a surface |
| 403 } while (hr == S_FALSE); | 410 // which is owned by ANGLE. As there are multiple devices involved in |
| 411 // this, the StretchRect call above is not synchronous. |
| 412 // We attempt to flush the batched operations to ensure that the picture is |
| 413 // copied to the surface owned by ANGLE. |
| 414 // We need to do this in a loop and call flush multiple times. |
| 415 // We have seen the GetData call for flushing the command buffer fail to |
| 416 // return success occassionally on multi core machines, leading to an |
| 417 // infinite loop. |
| 418 // Workaround is to have an upper limit of 10 on the number of iterations to |
| 419 // wait for the Flush to finish. |
| 420 int iterations = 0; |
| 421 while ((query_->GetData(NULL, 0, D3DGETDATA_FLUSH) == S_FALSE) && |
| 422 ++iterations < kMaxIterationsForD3DFlush) { |
| 423 Sleep(1); // Poor-man's Yield(). |
| 424 } |
| 404 eglBindTexImage( | 425 eglBindTexImage( |
| 405 static_cast<EGLDisplay*>(eglGetDisplay(EGL_DEFAULT_DISPLAY)), | 426 static_cast<EGLDisplay*>(eglGetDisplay(EGL_DEFAULT_DISPLAY)), |
| 406 decoding_surface_, | 427 decoding_surface_, |
| 407 EGL_BACK_BUFFER); | 428 EGL_BACK_BUFFER); |
| 408 } else { | 429 } else { |
| 409 scoped_array<char> bits; | 430 scoped_array<char> bits; |
| 410 RETURN_ON_FAILURE(GetBitmapFromSurface(DXVAVideoDecodeAccelerator::device_, | 431 RETURN_ON_FAILURE(GetBitmapFromSurface(DXVAVideoDecodeAccelerator::device_, |
| 411 dest_surface, &bits), | 432 dest_surface, &bits), |
| 412 "Failed to get bitmap from surface for rendering", | 433 "Failed to get bitmap from surface for rendering", |
| 413 false); | 434 false); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 | 511 |
| 491 hr = device_manager_->ResetDevice(device_, dev_manager_reset_token_); | 512 hr = device_manager_->ResetDevice(device_, dev_manager_reset_token_); |
| 492 RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false); | 513 RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false); |
| 493 | 514 |
| 494 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, &query_); | 515 hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, &query_); |
| 495 RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device query", false); | 516 RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device query", false); |
| 496 // Ensure query_ API works (to avoid an infinite loop later in | 517 // Ensure query_ API works (to avoid an infinite loop later in |
| 497 // CopyOutputSampleDataToPictureBuffer). | 518 // CopyOutputSampleDataToPictureBuffer). |
| 498 hr = query_->Issue(D3DISSUE_END); | 519 hr = query_->Issue(D3DISSUE_END); |
| 499 RETURN_ON_HR_FAILURE(hr, "Failed to issue END test query", false); | 520 RETURN_ON_HR_FAILURE(hr, "Failed to issue END test query", false); |
| 500 | |
| 501 return true; | 521 return true; |
| 502 } | 522 } |
| 503 | 523 |
| 504 DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator( | 524 DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator( |
| 505 media::VideoDecodeAccelerator::Client* client, | 525 media::VideoDecodeAccelerator::Client* client, |
| 506 const base::Callback<bool(void)>& make_context_current) | 526 const base::Callback<bool(void)>& make_context_current) |
| 507 : client_(client), | 527 : client_(client), |
| 508 egl_config_(NULL), | 528 egl_config_(NULL), |
| 509 state_(kUninitialized), | 529 state_(kUninitialized), |
| 510 pictures_requested_(false), | 530 pictures_requested_(false), |
| (...skipping 629 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1140 return; | 1160 return; |
| 1141 } | 1161 } |
| 1142 | 1162 |
| 1143 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( | 1163 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 1144 &DXVAVideoDecodeAccelerator::NotifyFlushDone, base::AsWeakPtr(this))); | 1164 &DXVAVideoDecodeAccelerator::NotifyFlushDone, base::AsWeakPtr(this))); |
| 1145 | 1165 |
| 1146 state_ = kNormal; | 1166 state_ = kNormal; |
| 1147 } | 1167 } |
| 1148 | 1168 |
| 1149 } // namespace content | 1169 } // namespace content |
| OLD | NEW |