| // Copyright 2019 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 "media/gpu/windows/d3d11_texture_selector.h" |
| |
| #include <d3d11.h> |
| |
| #include "base/feature_list.h" |
| #include "media/base/media_log.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/win/mf_helpers.h" |
| #include "media/gpu/windows/d3d11_copying_texture_wrapper.h" |
| #include "media/gpu/windows/d3d11_video_device_format_support.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace media { |
| |
| TextureSelector::TextureSelector(VideoPixelFormat pixfmt, |
| DXGI_FORMAT output_dxgifmt, |
| ComD3D11VideoDevice video_device, |
| ComD3D11DeviceContext device_context, |
| bool shared_image_use_shared_handle) |
| : pixel_format_(pixfmt), |
| output_dxgifmt_(output_dxgifmt), |
| video_device_(std::move(video_device)), |
| device_context_(std::move(device_context)), |
| shared_image_use_shared_handle_(shared_image_use_shared_handle) {} |
| |
| TextureSelector::~TextureSelector() = default; |
| |
| bool SupportsZeroCopy(const gpu::GpuPreferences& preferences, |
| const gpu::GpuDriverBugWorkarounds& workarounds) { |
| if (!preferences.enable_zero_copy_dxgi_video) |
| return false; |
| |
| if (workarounds.disable_dxgi_zero_copy_video) |
| return false; |
| |
| return true; |
| } |
| |
| // static |
| std::unique_ptr<TextureSelector> TextureSelector::Create( |
| const gpu::GpuPreferences& gpu_preferences, |
| const gpu::GpuDriverBugWorkarounds& workarounds, |
| DXGI_FORMAT decoder_output_format, |
| TextureSelector::HDRMode hdr_output_mode, |
| const FormatSupportChecker* format_checker, |
| ComD3D11VideoDevice video_device, |
| ComD3D11DeviceContext device_context, |
| MediaLog* media_log, |
| bool shared_image_use_shared_handle) { |
| VideoPixelFormat output_pixel_format; |
| DXGI_FORMAT output_dxgi_format; |
| absl::optional<gfx::ColorSpace> output_color_space; |
| |
| bool needs_texture_copy = !SupportsZeroCopy(gpu_preferences, workarounds); |
| |
| auto supports_fmt = [format_checker](auto fmt) { |
| return format_checker->CheckOutputFormatSupport(fmt); |
| }; |
| // TODO(liberato): add other options here, like "copy to rgb" for NV12. |
| switch (decoder_output_format) { |
| case DXGI_FORMAT_NV12: { |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder producing NV12"; |
| if (!needs_texture_copy || supports_fmt(DXGI_FORMAT_NV12)) { |
| output_pixel_format = PIXEL_FORMAT_NV12; |
| output_dxgi_format = DXGI_FORMAT_NV12; |
| // Leave |output_color_space| the same, since we'll bind either the |
| // original or the copy. Downstream will handle it, either in the |
| // shaders or in the overlay, if needed. |
| output_color_space.reset(); |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected NV12"; |
| } else if (supports_fmt(DXGI_FORMAT_B8G8R8A8_UNORM)) { |
| output_pixel_format = PIXEL_FORMAT_ARGB; |
| output_dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM; |
| output_color_space.reset(); |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected ARGB"; |
| } else { |
| MEDIA_LOG(INFO, media_log) << "NV12 not supported"; |
| return nullptr; |
| } |
| break; |
| } |
| case DXGI_FORMAT_P010: { |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder producing P010"; |
| if (hdr_output_mode == HDRMode::kSDROnly && |
| supports_fmt(DXGI_FORMAT_B8G8R8A8_UNORM)) { |
| output_dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM; |
| output_pixel_format = PIXEL_FORMAT_ARGB; |
| output_color_space = gfx::ColorSpace::CreateSRGB(); |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected ARGB"; |
| } else if (!needs_texture_copy || supports_fmt(DXGI_FORMAT_P010)) { |
| output_dxgi_format = DXGI_FORMAT_P010; |
| output_pixel_format = PIXEL_FORMAT_P016LE; |
| output_color_space.reset(); |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected P010"; |
| } else if (supports_fmt(DXGI_FORMAT_R16G16B16A16_FLOAT)) { |
| output_dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT; |
| output_pixel_format = PIXEL_FORMAT_RGBAF16; |
| output_color_space = gfx::ColorSpace::CreateSCRGBLinear(); |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected RGBAF16"; |
| } else if (supports_fmt(DXGI_FORMAT_R10G10B10A2_UNORM)) { |
| output_dxgi_format = DXGI_FORMAT_R10G10B10A2_UNORM; |
| output_pixel_format = PIXEL_FORMAT_XB30; |
| output_color_space = gfx::ColorSpace::CreateHDR10(); |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected XB30"; |
| } else { |
| MEDIA_LOG(INFO, media_log) << "P010 not supported"; |
| return nullptr; |
| } |
| break; |
| } |
| default: { |
| // TODO(tmathmeyer) support other profiles in the future. |
| MEDIA_LOG(INFO, media_log) |
| << "D3D11VideoDecoder does not support " << decoder_output_format; |
| return nullptr; |
| } |
| } |
| |
| // If we're trying to produce an output texture that's different from what |
| // the decoder is providing, then we need to copy it. If sharing decoder |
| // textures is not allowed, then copy either way. |
| needs_texture_copy |= (decoder_output_format != output_dxgi_format); |
| |
| MEDIA_LOG(INFO, media_log) |
| << "D3D11VideoDecoder output color space: " |
| << (output_color_space ? output_color_space->ToString() |
| : "(same as input)"); |
| |
| if (needs_texture_copy) { |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder is copying textures"; |
| return std::make_unique<CopyTextureSelector>( |
| output_pixel_format, decoder_output_format, output_dxgi_format, |
| output_color_space, std::move(video_device), std::move(device_context), |
| shared_image_use_shared_handle); |
| } else { |
| MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder is binding textures"; |
| // Binding can't change the color space. The consumer has to do it, if they |
| // want to. |
| DCHECK(!output_color_space); |
| return std::make_unique<TextureSelector>( |
| output_pixel_format, output_dxgi_format, std::move(video_device), |
| std::move(device_context), shared_image_use_shared_handle); |
| } |
| } |
| |
| std::unique_ptr<Texture2DWrapper> TextureSelector::CreateTextureWrapper( |
| ComD3D11Device device, |
| gfx::Size size) { |
| // TODO(liberato): If the output format is rgb, then create a pbuffer wrapper. |
| return std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat()); |
| } |
| |
| bool TextureSelector::DoesDecoderOutputUseSharedHandle() const { |
| return shared_image_use_shared_handle_; |
| } |
| |
| bool TextureSelector::WillCopyForTesting() const { |
| return false; |
| } |
| |
| CopyTextureSelector::CopyTextureSelector( |
| VideoPixelFormat pixfmt, |
| DXGI_FORMAT input_dxgifmt, |
| DXGI_FORMAT output_dxgifmt, |
| absl::optional<gfx::ColorSpace> output_color_space, |
| ComD3D11VideoDevice video_device, |
| ComD3D11DeviceContext device_context, |
| bool shared_image_use_shared_handle) |
| : TextureSelector(pixfmt, |
| output_dxgifmt, |
| std::move(video_device), |
| std::move(device_context), |
| shared_image_use_shared_handle), |
| output_color_space_(std::move(output_color_space)), |
| video_processor_proxy_( |
| base::MakeRefCounted<VideoProcessorProxy>(this->video_device(), |
| this->device_context())) {} |
| |
| CopyTextureSelector::~CopyTextureSelector() = default; |
| |
| std::unique_ptr<Texture2DWrapper> CopyTextureSelector::CreateTextureWrapper( |
| ComD3D11Device device, |
| gfx::Size size) { |
| D3D11_TEXTURE2D_DESC texture_desc = {}; |
| texture_desc.MipLevels = 1; |
| texture_desc.ArraySize = 1; |
| texture_desc.CPUAccessFlags = 0; |
| texture_desc.Format = output_dxgifmt_; |
| texture_desc.SampleDesc.Count = 1; |
| texture_desc.Usage = D3D11_USAGE_DEFAULT; |
| texture_desc.BindFlags = |
| D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; |
| texture_desc.Width = size.width(); |
| texture_desc.Height = size.height(); |
| if (DoesSharedImageUseSharedHandle()) { |
| texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | |
| D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; |
| } |
| |
| ComD3D11Texture2D out_texture; |
| if (FAILED(device->CreateTexture2D(&texture_desc, nullptr, &out_texture))) |
| return nullptr; |
| |
| if (FAILED( |
| SetDebugName(out_texture.Get(), "D3D11Decoder_CopyTextureSelector"))) |
| return nullptr; |
| |
| return std::make_unique<CopyingTexture2DWrapper>( |
| size, std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat()), |
| video_processor_proxy_, out_texture, output_color_space_); |
| } |
| |
| bool CopyTextureSelector::DoesDecoderOutputUseSharedHandle() const { |
| return false; |
| } |
| |
| bool CopyTextureSelector::WillCopyForTesting() const { |
| return true; |
| } |
| |
| } // namespace media |