Commit eec86ff6 authored by Sunny Sachanandani's avatar Sunny Sachanandani Committed by Commit Bot

Reenable direct composition without video layers

Reenable direct composition surface on devices without hardware overlays
since it enables other power improvements like flip mode swap chain and
disabling DWM redirection surface.

To mitigate the original concerns about direct composition, this change
makes DCLayerTree lazy initialize the video context and processor when
a video layer is first used.  On devices that don't support hardware
overlays, the video context and processor will never be initialized.

Bug: 894675, 900702
Change-Id: I8c9d5b78718de2620ca177410d4916390e7cb219
Reviewed-on: https://chromium-review.googlesource.com/c/1318808
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: default avatarZhenyao Mo <zmo@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarAntoine Labour <piman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606312}
parent 7b104ecc
...@@ -75,8 +75,8 @@ WebUIDataSource* CreateGpuHTMLSource() { ...@@ -75,8 +75,8 @@ WebUIDataSource* CreateGpuHTMLSource() {
} }
std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair( std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
const std::string& desc, base::StringPiece desc,
const std::string& value) { base::StringPiece value) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetString("description", desc); dict->SetString("description", desc);
dict->SetString("value", value); dict->SetString("value", value);
...@@ -84,7 +84,7 @@ std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair( ...@@ -84,7 +84,7 @@ std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
} }
std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair( std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
const std::string& desc, base::StringPiece desc,
std::unique_ptr<base::Value> value) { std::unique_ptr<base::Value> value) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetString("description", desc); dict->SetString("description", desc);
...@@ -181,8 +181,11 @@ std::unique_ptr<base::ListValue> BasicGpuInfoAsListValue( ...@@ -181,8 +181,11 @@ std::unique_ptr<base::ListValue> BasicGpuInfoAsListValue(
NewDescriptionValuePair("Desktop compositing", compositor)); NewDescriptionValuePair("Desktop compositing", compositor));
basic_info->Append(NewDescriptionValuePair( basic_info->Append(NewDescriptionValuePair(
"Direct composition overlays", "Direct composition",
std::make_unique<base::Value>(gpu_info.direct_composition_overlays))); std::make_unique<base::Value>(gpu_info.direct_composition)));
basic_info->Append(NewDescriptionValuePair(
"Supports overlays",
std::make_unique<base::Value>(gpu_info.supports_overlays)));
auto overlay_capabilities = std::make_unique<base::ListValue>(); auto overlay_capabilities = std::make_unique<base::ListValue>();
for (const auto& cap : gpu_info.overlay_capabilities) { for (const auto& cap : gpu_info.overlay_capabilities) {
......
...@@ -1353,7 +1353,7 @@ media::GpuVideoAcceleratorFactories* RenderThreadImpl::GetGpuFactories() { ...@@ -1353,7 +1353,7 @@ media::GpuVideoAcceleratorFactories* RenderThreadImpl::GetGpuFactories() {
enable_video_gpu_memory_buffers = enable_video_gpu_memory_buffers =
enable_video_gpu_memory_buffers && enable_video_gpu_memory_buffers &&
(cmd_line->HasSwitch(switches::kEnableGpuMemoryBufferVideoFrames) || (cmd_line->HasSwitch(switches::kEnableGpuMemoryBufferVideoFrames) ||
gpu_channel_host->gpu_info().direct_composition_overlays); gpu_channel_host->gpu_info().supports_overlays);
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
media::mojom::InterfaceFactoryPtr interface_factory; media::mojom::InterfaceFactoryPtr interface_factory;
......
...@@ -1387,6 +1387,21 @@ ...@@ -1387,6 +1387,21 @@
"max_texture_size_limit_4096" "max_texture_size_limit_4096"
] ]
}, },
{
"id": 149,
"description": "Direct composition flashes black initially on Win <10",
"cr_bugs": [588588],
"os": {
"type": "win",
"version": {
"op": "<",
"value": "10.0"
}
},
"features": [
"disable_direct_composition"
]
},
{ {
"id": 150, "id": 150,
"cr_bugs": [563714], "cr_bugs": [563714],
...@@ -2574,6 +2589,32 @@ ...@@ -2574,6 +2589,32 @@
"GL_EXT_disjoint_timer_query" "GL_EXT_disjoint_timer_query"
] ]
}, },
{
"id": 248,
"description": "Direct composition causes slow presents on Intel Sandybridge",
"cr_bugs": [775898, 785648],
"os": {
"type": "win"
},
"vendor_id": "0x8086",
"device_id": ["0x0116"],
"features": [
"disable_direct_composition"
]
},
{
"id": 249,
"description": "Direct composition causes slow presents on old Nvidia GPUs",
"cr_bugs": [775898],
"os": {
"type": "win"
},
"vendor_id": "0x10de",
"device_id": ["0x10d8"],
"features": [
"disable_direct_composition"
]
},
{ {
"id": 250, "id": 250,
"description": "Depth/stencil renderbuffers can't be resized on NVIDIA on early macOS 10.13", "description": "Depth/stencil renderbuffers can't be resized on NVIDIA on early macOS 10.13",
...@@ -2909,6 +2950,35 @@ ...@@ -2909,6 +2950,35 @@
"disable_aimagereader" "disable_aimagereader"
] ]
}, },
{
"id": 277,
"description": "Direct composition path is buggy on certain AMD devices/drivers",
"cr_bugs": [800950],
"os": {
"type": "win"
},
"vendor_id": "0x1002",
"driver_version": {
"op": "=",
"value": "8.970.100.9001"
},
"features": [
"disable_direct_composition"
]
},
{
"id": 278,
"description": "Direct composition path is buggy on certain AMD devices/drivers",
"cr_bugs": [800950],
"os": {
"type": "win"
},
"vendor_id": "0x1002",
"device_id": ["0x6900"],
"features": [
"disable_direct_composition"
]
},
{ {
"id": 279, "id": 279,
"description": "WindowServer crashes on VMWare bots using CA renderer", "description": "WindowServer crashes on VMWare bots using CA renderer",
......
...@@ -188,7 +188,8 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { ...@@ -188,7 +188,8 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const {
bool passthrough_cmd_decoder; bool passthrough_cmd_decoder;
bool can_support_threaded_texture_mailbox; bool can_support_threaded_texture_mailbox;
#if defined(OS_WIN) #if defined(OS_WIN)
bool direct_composition_overlays; bool direct_composition;
bool supports_overlays;
OverlayCapabilities overlay_capabilities; OverlayCapabilities overlay_capabilities;
DxDiagNode dx_diagnostics; DxDiagNode dx_diagnostics;
Dx12VulkanVersionInfo dx12_vulkan_version_info; Dx12VulkanVersionInfo dx12_vulkan_version_info;
...@@ -247,7 +248,8 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const { ...@@ -247,7 +248,8 @@ void GPUInfo::EnumerateFields(Enumerator* enumerator) const {
can_support_threaded_texture_mailbox); can_support_threaded_texture_mailbox);
// TODO(kbr): add dx_diagnostics on Windows. // TODO(kbr): add dx_diagnostics on Windows.
#if defined(OS_WIN) #if defined(OS_WIN)
enumerator->AddBool("directCompositionOverlays", direct_composition_overlays); enumerator->AddBool("directComposition", direct_composition);
enumerator->AddBool("supportsOverlays", supports_overlays);
for (const auto& cap : overlay_capabilities) for (const auto& cap : overlay_capabilities)
EnumerateOverlayCapability(cap, enumerator); EnumerateOverlayCapability(cap, enumerator);
EnumerateDx12VulkanVersionInfo(dx12_vulkan_version_info, enumerator); EnumerateDx12VulkanVersionInfo(dx12_vulkan_version_info, enumerator);
......
...@@ -275,8 +275,11 @@ struct GPU_EXPORT GPUInfo { ...@@ -275,8 +275,11 @@ struct GPU_EXPORT GPUInfo {
bool can_support_threaded_texture_mailbox = false; bool can_support_threaded_texture_mailbox = false;
#if defined(OS_WIN) #if defined(OS_WIN)
// True if we use direct composition surface on Windows.
bool direct_composition = false;
// True if we use direct composition surface overlays on Windows. // True if we use direct composition surface overlays on Windows.
bool direct_composition_overlays = false; bool supports_overlays = false;
OverlayCapabilities overlay_capabilities; OverlayCapabilities overlay_capabilities;
......
...@@ -229,7 +229,7 @@ void AppendWorkaroundsToCommandLine(const GpuFeatureInfo& gpu_feature_info, ...@@ -229,7 +229,7 @@ void AppendWorkaroundsToCommandLine(const GpuFeatureInfo& gpu_feature_info,
command_line->AppendSwitch(switches::kDisableES3GLContext); command_line->AppendSwitch(switches::kDisableES3GLContext);
} }
if (gpu_feature_info.IsWorkaroundEnabled(DISABLE_DIRECT_COMPOSITION)) { if (gpu_feature_info.IsWorkaroundEnabled(DISABLE_DIRECT_COMPOSITION)) {
command_line->AppendSwitch(switches::kDisableDirectCompositionLayers); command_line->AppendSwitch(switches::kDisableDirectComposition);
} }
} }
......
...@@ -128,7 +128,9 @@ struct GpuInfo { ...@@ -128,7 +128,9 @@ struct GpuInfo {
bool can_support_threaded_texture_mailbox; bool can_support_threaded_texture_mailbox;
[EnableIf=is_win] [EnableIf=is_win]
bool direct_composition_overlays; bool direct_composition;
[EnableIf=is_win]
bool supports_overlays;
[EnableIf=is_win] [EnableIf=is_win]
array<OverlayCapability> overlay_capabilities; array<OverlayCapability> overlay_capabilities;
[EnableIf=is_win] [EnableIf=is_win]
......
...@@ -295,7 +295,8 @@ bool StructTraits<gpu::mojom::GpuInfoDataView, gpu::GPUInfo>::Read( ...@@ -295,7 +295,8 @@ bool StructTraits<gpu::mojom::GpuInfoDataView, gpu::GPUInfo>::Read(
out->oop_rasterization_supported = data.oop_rasterization_supported(); out->oop_rasterization_supported = data.oop_rasterization_supported();
#if defined(OS_WIN) #if defined(OS_WIN)
out->direct_composition_overlays = data.direct_composition_overlays(); out->direct_composition = data.direct_composition();
out->supports_overlays = data.supports_overlays();
#endif #endif
return data.ReadInitializationTime(&out->initialization_time) && return data.ReadInitializationTime(&out->initialization_time) &&
......
...@@ -286,8 +286,12 @@ struct StructTraits<gpu::mojom::GpuInfoDataView, gpu::GPUInfo> { ...@@ -286,8 +286,12 @@ struct StructTraits<gpu::mojom::GpuInfoDataView, gpu::GPUInfo> {
} }
#if defined(OS_WIN) #if defined(OS_WIN)
static bool direct_composition_overlays(const gpu::GPUInfo& input) { static bool direct_composition(const gpu::GPUInfo& input) {
return input.direct_composition_overlays; return input.direct_composition;
}
static bool supports_overlays(const gpu::GPUInfo& input) {
return input.supports_overlays;
} }
static const gpu::OverlayCapabilities& overlay_capabilities( static const gpu::OverlayCapabilities& overlay_capabilities(
......
...@@ -154,7 +154,8 @@ TEST_F(StructTraitsTest, GpuInfo) { ...@@ -154,7 +154,8 @@ TEST_F(StructTraitsTest, GpuInfo) {
const bool in_process_gpu = true; const bool in_process_gpu = true;
const bool passthrough_cmd_decoder = true; const bool passthrough_cmd_decoder = true;
#if defined(OS_WIN) #if defined(OS_WIN)
const bool direct_composition_overlays = true; const bool direct_composition = true;
const bool supports_overlays = true;
const gpu::OverlayCapabilities overlay_capabilities = { const gpu::OverlayCapabilities overlay_capabilities = {
{OverlayFormat::kBGRA, false}, {OverlayFormat::kNV12, true}}; {OverlayFormat::kBGRA, false}, {OverlayFormat::kNV12, true}};
const DxDiagNode dx_diagnostics; const DxDiagNode dx_diagnostics;
...@@ -197,7 +198,8 @@ TEST_F(StructTraitsTest, GpuInfo) { ...@@ -197,7 +198,8 @@ TEST_F(StructTraitsTest, GpuInfo) {
input.in_process_gpu = in_process_gpu; input.in_process_gpu = in_process_gpu;
input.passthrough_cmd_decoder = passthrough_cmd_decoder; input.passthrough_cmd_decoder = passthrough_cmd_decoder;
#if defined(OS_WIN) #if defined(OS_WIN)
input.direct_composition_overlays = direct_composition_overlays; input.direct_composition = direct_composition;
input.supports_overlays = supports_overlays;
input.overlay_capabilities = overlay_capabilities; input.overlay_capabilities = overlay_capabilities;
input.dx_diagnostics = dx_diagnostics; input.dx_diagnostics = dx_diagnostics;
#endif #endif
...@@ -255,7 +257,8 @@ TEST_F(StructTraitsTest, GpuInfo) { ...@@ -255,7 +257,8 @@ TEST_F(StructTraitsTest, GpuInfo) {
EXPECT_EQ(in_process_gpu, output.in_process_gpu); EXPECT_EQ(in_process_gpu, output.in_process_gpu);
EXPECT_EQ(passthrough_cmd_decoder, output.passthrough_cmd_decoder); EXPECT_EQ(passthrough_cmd_decoder, output.passthrough_cmd_decoder);
#if defined(OS_WIN) #if defined(OS_WIN)
EXPECT_EQ(direct_composition_overlays, output.direct_composition_overlays); EXPECT_EQ(direct_composition, output.direct_composition);
EXPECT_EQ(supports_overlays, output.supports_overlays);
EXPECT_EQ(overlay_capabilities, output.overlay_capabilities); EXPECT_EQ(overlay_capabilities, output.overlay_capabilities);
EXPECT_EQ(dx_diagnostics.values, output.dx_diagnostics.values); EXPECT_EQ(dx_diagnostics.values, output.dx_diagnostics.values);
#endif #endif
......
...@@ -139,65 +139,18 @@ OverlaySupportInfo g_overlay_support_info[] = { ...@@ -139,65 +139,18 @@ OverlaySupportInfo g_overlay_support_info[] = {
{OverlayFormat::kBGRA, DXGI_FORMAT_B8G8R8A8_UNORM, 0}, {OverlayFormat::kBGRA, DXGI_FORMAT_B8G8R8A8_UNORM, 0},
}; };
bool IsDirectCompositionSupported() {
static bool initialized = false;
static bool supported = false;
if (initialized)
return supported;
initialized = true;
// Before Windows 10 Anniversary Update (Redstone 1), overlay planes wouldn't
// be assigned to non-UWP apps.
if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1)
return false;
// Blacklist direct composition if MCTU.dll or MCTUX.dll are injected. These
// are user mode drivers for display adapters from Magic Control Technology
// Corporation.
if (GetModuleHandle(TEXT("MCTU.dll")) || GetModuleHandle(TEXT("MCTUX.dll"))) {
DLOG(ERROR) << "Blacklisted due to third party modules";
return false;
}
// Flexible surface compatibility is required to be able to MakeCurrent with
// the default pbuffer surface.
if (!gl::GLSurfaceEGL::IsEGLFlexibleSurfaceCompatibilitySupported()) {
DLOG(ERROR) << "EGL_ANGLE_flexible_surface_compatibility not supported";
return false;
}
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
gl::QueryD3D11DeviceObjectFromANGLE();
if (!d3d11_device) {
DLOG(ERROR) << "Failed to retrieve D3D11 device";
return false;
}
// This will fail if DirectComposition DLL can't be loaded.
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device =
gl::QueryDirectCompositionDevice(d3d11_device);
if (!dcomp_device) {
DLOG(ERROR) << "Failed to retrieve direct composition device";
return false;
}
// This will fail if the D3D device is "Microsoft Basic Display Adapter".
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
if (FAILED(d3d11_device.CopyTo(video_device.GetAddressOf()))) {
DLOG(ERROR) << "Failed to retrieve video device";
return false;
}
supported = true;
return true;
}
void InitializeHardwareOverlaySupport() { void InitializeHardwareOverlaySupport() {
if (g_overlay_support_initialized) if (g_overlay_support_initialized)
return; return;
g_overlay_support_initialized = true; g_overlay_support_initialized = true;
if (!IsDirectCompositionSupported()) // Check for DirectComposition support first to prevent likely crashes.
if (!DirectCompositionSurfaceWin::IsDirectCompositionSupported())
return;
// Before Windows 10 Anniversary Update (Redstone 1), overlay planes wouldn't
// be assigned to non-UWP apps.
if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1)
return; return;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
...@@ -219,6 +172,13 @@ void InitializeHardwareOverlaySupport() { ...@@ -219,6 +172,13 @@ void InitializeHardwareOverlaySupport() {
return; return;
} }
// This will fail if the D3D device is "Microsoft Basic Display Adapter".
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
if (FAILED(d3d11_device.CopyTo(video_device.GetAddressOf()))) {
DLOG(ERROR) << "Failed to retrieve video device";
return;
}
bool supports_nv12_rec709 = false; bool supports_nv12_rec709 = false;
unsigned int i = 0; unsigned int i = 0;
while (true) { while (true) {
...@@ -404,6 +364,14 @@ class DCLayerTree { ...@@ -404,6 +364,14 @@ class DCLayerTree {
bool InitializeVideoProcessor(const gfx::Size& input_size, bool InitializeVideoProcessor(const gfx::Size& input_size,
const gfx::Size& output_size); const gfx::Size& output_size);
const Microsoft::WRL::ComPtr<ID3D11VideoDevice>& video_device() const {
return video_device_;
}
const Microsoft::WRL::ComPtr<ID3D11VideoContext>& video_context() const {
return video_context_;
}
const Microsoft::WRL::ComPtr<ID3D11VideoProcessor>& video_processor() const { const Microsoft::WRL::ComPtr<ID3D11VideoProcessor>& video_processor() const {
return video_processor_; return video_processor_;
} }
...@@ -468,9 +436,7 @@ class DCLayerTree::SwapChainPresenter { ...@@ -468,9 +436,7 @@ class DCLayerTree::SwapChainPresenter {
public: public:
SwapChainPresenter(DCLayerTree* layer_tree, SwapChainPresenter(DCLayerTree* layer_tree,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device, Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device, Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device);
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context);
~SwapChainPresenter(); ~SwapChainPresenter();
// Present the given overlay to swap chain. |needs_commit| is true if direct // Present the given overlay to swap chain. |needs_commit| is true if direct
...@@ -493,12 +459,6 @@ class DCLayerTree::SwapChainPresenter { ...@@ -493,12 +459,6 @@ class DCLayerTree::SwapChainPresenter {
bool UploadVideoImages(gl::GLImageMemory* y_image_memory, bool UploadVideoImages(gl::GLImageMemory* y_image_memory,
gl::GLImageMemory* uv_image_memory); gl::GLImageMemory* uv_image_memory);
// Initialize video processor to handle at least the given input and output
// size. Can reuse existing video processor if it's large enough. Returns
// true on success.
bool InitializeVideoProcessor(const gfx::Size& in_size,
const gfx::Size& out_size);
// Recreate swap chain using given size. Use preferred YUV format if |yuv| is // Recreate swap chain using given size. Use preferred YUV format if |yuv| is
// true, or BGRA otherwise. Set protected video flags if |protected_video| is // true, or BGRA otherwise. Set protected video flags if |protected_video| is
// true. Returns true on success. // true. Returns true on success.
...@@ -540,10 +500,6 @@ class DCLayerTree::SwapChainPresenter { ...@@ -540,10 +500,6 @@ class DCLayerTree::SwapChainPresenter {
// Current size of swap chain. // Current size of swap chain.
gfx::Size swap_chain_size_; gfx::Size swap_chain_size_;
// Current minimum input and output size of the video processor.
gfx::Size processor_input_size_;
gfx::Size processor_output_size_;
// Whether the current swap chain is using the preferred YUV format. // Whether the current swap chain is using the preferred YUV format.
bool is_yuv_swapchain_ = false; bool is_yuv_swapchain_ = false;
...@@ -592,12 +548,6 @@ class DCLayerTree::SwapChainPresenter { ...@@ -592,12 +548,6 @@ class DCLayerTree::SwapChainPresenter {
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_; Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_;
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_; Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorOutputView> out_view_;
Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor_;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator>
video_processor_enumerator_;
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device_;
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context_;
// Handle returned by DCompositionCreateSurfaceHandle() used to create swap // Handle returned by DCompositionCreateSurfaceHandle() used to create swap
// chain that can be used for direct composition. // chain that can be used for direct composition.
...@@ -615,19 +565,6 @@ bool DCLayerTree::Initialize( ...@@ -615,19 +565,6 @@ bool DCLayerTree::Initialize(
DCHECK(dcomp_device); DCHECK(dcomp_device);
dcomp_device_ = std::move(dcomp_device); dcomp_device_ = std::move(dcomp_device);
// This can fail if the D3D device is "Microsoft Basic Display Adapter".
if (FAILED(d3d11_device_.CopyTo(video_device_.GetAddressOf()))) {
DLOG(ERROR) << "Failed to retrieve video device from D3D11 device";
return false;
}
DCHECK(video_device_);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
d3d11_device_->GetImmediateContext(context.GetAddressOf());
DCHECK(context);
context.CopyTo(video_context_.GetAddressOf());
DCHECK(video_context_);
Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device; Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device;
dcomp_device_.CopyTo(desktop_device.GetAddressOf()); dcomp_device_.CopyTo(desktop_device.GetAddressOf());
DCHECK(desktop_device); DCHECK(desktop_device);
...@@ -648,6 +585,21 @@ bool DCLayerTree::Initialize( ...@@ -648,6 +585,21 @@ bool DCLayerTree::Initialize(
bool DCLayerTree::InitializeVideoProcessor(const gfx::Size& input_size, bool DCLayerTree::InitializeVideoProcessor(const gfx::Size& input_size,
const gfx::Size& output_size) { const gfx::Size& output_size) {
if (!video_device_) {
// This can fail if the D3D device is "Microsoft Basic Display Adapter".
if (FAILED(d3d11_device_.CopyTo(video_device_.GetAddressOf()))) {
DLOG(ERROR) << "Failed to retrieve video device from D3D11 device";
return false;
}
DCHECK(video_device_);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
d3d11_device_->GetImmediateContext(context.GetAddressOf());
DCHECK(context);
context.CopyTo(video_context_.GetAddressOf());
DCHECK(video_context_);
}
if (video_processor_ && SizeContains(video_input_size_, input_size) && if (video_processor_ && SizeContains(video_input_size_, input_size) &&
SizeContains(video_output_size_, output_size)) SizeContains(video_output_size_, output_size))
return true; return true;
...@@ -699,14 +651,10 @@ DCLayerTree::GetLayerSwapChainForTesting(size_t index) const { ...@@ -699,14 +651,10 @@ DCLayerTree::GetLayerSwapChainForTesting(size_t index) const {
DCLayerTree::SwapChainPresenter::SwapChainPresenter( DCLayerTree::SwapChainPresenter::SwapChainPresenter(
DCLayerTree* layer_tree, DCLayerTree* layer_tree,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device, Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device, Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device)
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context)
: layer_tree_(layer_tree), : layer_tree_(layer_tree),
d3d11_device_(d3d11_device), d3d11_device_(d3d11_device),
dcomp_device_(dcomp_device), dcomp_device_(dcomp_device) {}
video_device_(video_device),
video_context_(video_context) {}
DCLayerTree::SwapChainPresenter::~SwapChainPresenter() {} DCLayerTree::SwapChainPresenter::~SwapChainPresenter() {}
...@@ -1011,7 +959,6 @@ bool DCLayerTree::SwapChainPresenter::PresentToSwapChain( ...@@ -1011,7 +959,6 @@ bool DCLayerTree::SwapChainPresenter::PresentToSwapChain(
swap_chain_size_ = swap_chain_size; swap_chain_size_ = swap_chain_size;
swap_chain_handle_.Close(); swap_chain_handle_.Close();
swap_chain_.Reset(); swap_chain_.Reset();
out_view_.Reset();
content_visual_->SetContent(nullptr); content_visual_->SetContent(nullptr);
*needs_commit = true; *needs_commit = true;
} }
...@@ -1142,50 +1089,19 @@ bool DCLayerTree::SwapChainPresenter::PresentToSwapChain( ...@@ -1142,50 +1089,19 @@ bool DCLayerTree::SwapChainPresenter::PresentToSwapChain(
return true; return true;
} }
bool DCLayerTree::SwapChainPresenter::InitializeVideoProcessor(
const gfx::Size& in_size,
const gfx::Size& out_size) {
if (video_processor_ && SizeContains(processor_input_size_, in_size) &&
SizeContains(processor_output_size_, out_size))
return true;
processor_input_size_ = in_size;
processor_output_size_ = out_size;
if (!layer_tree_->InitializeVideoProcessor(in_size, out_size))
return false;
video_processor_enumerator_ = layer_tree_->video_processor_enumerator();
video_processor_ = layer_tree_->video_processor();
// out_view_ depends on video_processor_enumerator_, so ensure it's
// recreated if the enumerator is.
out_view_.Reset();
return true;
}
bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt( bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt(
Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture, Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture,
UINT input_level, UINT input_level,
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex, Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex,
const gfx::Size& input_size, const gfx::Size& input_size,
const gfx::ColorSpace& src_color_space) { const gfx::ColorSpace& src_color_space) {
if (!InitializeVideoProcessor(input_size, swap_chain_size_)) if (!layer_tree_->InitializeVideoProcessor(input_size, swap_chain_size_))
return false; return false;
if (!out_view_) { Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context =
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture; layer_tree_->video_context();
swap_chain_->GetBuffer(0, IID_PPV_ARGS(texture.GetAddressOf())); Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor =
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC out_desc = {}; layer_tree_->video_processor();
out_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
out_desc.Texture2D.MipSlice = 0;
HRESULT hr = video_device_->CreateVideoProcessorOutputView(
texture.Get(), video_processor_enumerator_.Get(), &out_desc,
out_view_.GetAddressOf());
if (FAILED(hr)) {
DLOG(ERROR) << "CreateVideoProcessorOutputView failed with error 0x"
<< std::hex << hr;
return false;
}
}
gfx::ColorSpace output_color_space = gfx::ColorSpace output_color_space =
is_yuv_swapchain_ ? src_color_space : gfx::ColorSpace::CreateSRGB(); is_yuv_swapchain_ ? src_color_space : gfx::ColorSpace::CreateSRGB();
...@@ -1198,19 +1114,19 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt( ...@@ -1198,19 +1114,19 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt(
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3; Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3;
Microsoft::WRL::ComPtr<ID3D11VideoContext1> context1; Microsoft::WRL::ComPtr<ID3D11VideoContext1> context1;
if (SUCCEEDED(swap_chain_.CopyTo(swap_chain3.GetAddressOf())) && if (SUCCEEDED(swap_chain_.CopyTo(swap_chain3.GetAddressOf())) &&
SUCCEEDED(video_context_.CopyTo(context1.GetAddressOf()))) { SUCCEEDED(video_context.CopyTo(context1.GetAddressOf()))) {
DCHECK(swap_chain3); DCHECK(swap_chain3);
DCHECK(context1); DCHECK(context1);
// Set input color space. // Set input color space.
context1->VideoProcessorSetStreamColorSpace1( context1->VideoProcessorSetStreamColorSpace1(
video_processor_.Get(), 0, video_processor.Get(), 0,
gfx::ColorSpaceWin::GetDXGIColorSpace(src_color_space)); gfx::ColorSpaceWin::GetDXGIColorSpace(src_color_space));
// Set output color space. // Set output color space.
DXGI_COLOR_SPACE_TYPE output_dxgi_color_space = DXGI_COLOR_SPACE_TYPE output_dxgi_color_space =
gfx::ColorSpaceWin::GetDXGIColorSpace( gfx::ColorSpaceWin::GetDXGIColorSpace(
output_color_space, is_yuv_swapchain_ /* force_yuv */); output_color_space, is_yuv_swapchain_ /* force_yuv */);
if (SUCCEEDED(swap_chain3->SetColorSpace1(output_dxgi_color_space))) { if (SUCCEEDED(swap_chain3->SetColorSpace1(output_dxgi_color_space))) {
context1->VideoProcessorSetOutputColorSpace1(video_processor_.Get(), context1->VideoProcessorSetOutputColorSpace1(video_processor.Get(),
output_dxgi_color_space); output_dxgi_color_space);
} }
} else { } else {
...@@ -1218,12 +1134,12 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt( ...@@ -1218,12 +1134,12 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt(
// only if ID3D11VideoContext1 isn't available. // only if ID3D11VideoContext1 isn't available.
D3D11_VIDEO_PROCESSOR_COLOR_SPACE src_d3d11_color_space = D3D11_VIDEO_PROCESSOR_COLOR_SPACE src_d3d11_color_space =
gfx::ColorSpaceWin::GetD3D11ColorSpace(src_color_space); gfx::ColorSpaceWin::GetD3D11ColorSpace(src_color_space);
video_context_->VideoProcessorSetStreamColorSpace(video_processor_.Get(), 0, video_context->VideoProcessorSetStreamColorSpace(video_processor.Get(), 0,
&src_d3d11_color_space); &src_d3d11_color_space);
D3D11_VIDEO_PROCESSOR_COLOR_SPACE output_d3d11_color_space = D3D11_VIDEO_PROCESSOR_COLOR_SPACE output_d3d11_color_space =
gfx::ColorSpaceWin::GetD3D11ColorSpace(output_color_space); gfx::ColorSpaceWin::GetD3D11ColorSpace(output_color_space);
video_context_->VideoProcessorSetOutputColorSpace( video_context->VideoProcessorSetOutputColorSpace(video_processor.Get(),
video_processor_.Get(), &output_d3d11_color_space); &output_d3d11_color_space);
} }
{ {
...@@ -1243,12 +1159,18 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt( ...@@ -1243,12 +1159,18 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt(
release_keyed_mutex.emplace(keyed_mutex, 0); release_keyed_mutex.emplace(keyed_mutex, 0);
} }
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device =
layer_tree_->video_device();
Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator>
video_processor_enumerator = layer_tree_->video_processor_enumerator();
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC in_desc = {}; D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC in_desc = {};
in_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; in_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
in_desc.Texture2D.ArraySlice = input_level; in_desc.Texture2D.ArraySlice = input_level;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorInputView> in_view; Microsoft::WRL::ComPtr<ID3D11VideoProcessorInputView> in_view;
HRESULT hr = video_device_->CreateVideoProcessorInputView( HRESULT hr = video_device->CreateVideoProcessorInputView(
input_texture.Get(), video_processor_enumerator_.Get(), &in_desc, input_texture.Get(), video_processor_enumerator.Get(), &in_desc,
in_view.GetAddressOf()); in_view.GetAddressOf());
if (FAILED(hr)) { if (FAILED(hr)) {
DLOG(ERROR) << "CreateVideoProcessorInputView failed with error 0x" DLOG(ERROR) << "CreateVideoProcessorInputView failed with error 0x"
...@@ -1264,16 +1186,32 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt( ...@@ -1264,16 +1186,32 @@ bool DCLayerTree::SwapChainPresenter::VideoProcessorBlt(
stream.FutureFrames = 0; stream.FutureFrames = 0;
stream.pInputSurface = in_view.Get(); stream.pInputSurface = in_view.Get();
RECT dest_rect = gfx::Rect(swap_chain_size_).ToRECT(); RECT dest_rect = gfx::Rect(swap_chain_size_).ToRECT();
video_context_->VideoProcessorSetOutputTargetRect(video_processor_.Get(), video_context->VideoProcessorSetOutputTargetRect(video_processor.Get(),
TRUE, &dest_rect); TRUE, &dest_rect);
video_context_->VideoProcessorSetStreamDestRect(video_processor_.Get(), 0, video_context->VideoProcessorSetStreamDestRect(video_processor.Get(), 0,
TRUE, &dest_rect); TRUE, &dest_rect);
RECT source_rect = gfx::Rect(input_size).ToRECT(); RECT source_rect = gfx::Rect(input_size).ToRECT();
video_context_->VideoProcessorSetStreamSourceRect(video_processor_.Get(), 0, video_context->VideoProcessorSetStreamSourceRect(video_processor.Get(), 0,
TRUE, &source_rect); TRUE, &source_rect);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
swap_chain_->GetBuffer(0, IID_PPV_ARGS(texture.GetAddressOf()));
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC out_desc = {};
out_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
out_desc.Texture2D.MipSlice = 0;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorOutputView> out_view;
hr = video_device->CreateVideoProcessorOutputView(
texture.Get(), video_processor_enumerator.Get(), &out_desc,
out_view.GetAddressOf());
if (FAILED(hr)) {
DLOG(ERROR) << "CreateVideoProcessorOutputView failed with error 0x"
<< std::hex << hr;
return false;
}
hr = video_context_->VideoProcessorBlt(video_processor_.Get(), hr = video_context->VideoProcessorBlt(video_processor.Get(), out_view.Get(),
out_view_.Get(), 0, 1, &stream); 0, 1, &stream);
if (FAILED(hr)) { if (FAILED(hr)) {
DLOG(ERROR) << "VideoProcessorBlt failed with error 0x" << std::hex << hr; DLOG(ERROR) << "VideoProcessorBlt failed with error 0x" << std::hex << hr;
return false; return false;
...@@ -1290,7 +1228,6 @@ bool DCLayerTree::SwapChainPresenter::ReallocateSwapChain( ...@@ -1290,7 +1228,6 @@ bool DCLayerTree::SwapChainPresenter::ReallocateSwapChain(
TRACE_EVENT0("gpu", "DCLayerTree::SwapChainPresenter::ReallocateSwapChain"); TRACE_EVENT0("gpu", "DCLayerTree::SwapChainPresenter::ReallocateSwapChain");
swap_chain_size_ = swap_chain_size; swap_chain_size_ = swap_chain_size;
out_view_.Reset();
swap_chain_.Reset(); swap_chain_.Reset();
swap_chain_handle_.Close(); swap_chain_handle_.Close();
...@@ -1430,7 +1367,7 @@ bool DCLayerTree::CommitAndClearPendingOverlays( ...@@ -1430,7 +1367,7 @@ bool DCLayerTree::CommitAndClearPendingOverlays(
new_video_swap_chains.emplace_back(std::move(video_swap_chains_[i])); new_video_swap_chains.emplace_back(std::move(video_swap_chains_[i]));
} else { } else {
new_video_swap_chains.emplace_back(std::make_unique<SwapChainPresenter>( new_video_swap_chains.emplace_back(std::make_unique<SwapChainPresenter>(
this, d3d11_device_, dcomp_device_, video_device_, video_context_)); this, d3d11_device_, dcomp_device_));
} }
} }
video_swap_chains_.swap(new_video_swap_chains); video_swap_chains_.swap(new_video_swap_chains);
...@@ -1507,11 +1444,50 @@ DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() { ...@@ -1507,11 +1444,50 @@ DirectCompositionSurfaceWin::~DirectCompositionSurfaceWin() {
} }
// static // static
bool DirectCompositionSurfaceWin::AreOverlaysSupported() { bool DirectCompositionSurfaceWin::IsDirectCompositionSupported() {
// Check for DirectComposition support first to prevent likely crashes. static const bool supported = [] {
if (!IsDirectCompositionSupported()) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
return false; if (command_line->HasSwitch(switches::kDisableDirectComposition))
return false;
// Blacklist direct composition if MCTU.dll or MCTUX.dll are injected. These
// are user mode drivers for display adapters from Magic Control Technology
// Corporation.
if (GetModuleHandle(TEXT("MCTU.dll")) ||
GetModuleHandle(TEXT("MCTUX.dll"))) {
DLOG(ERROR) << "Blacklisted due to third party modules";
return false;
}
// Flexible surface compatibility is required to be able to MakeCurrent with
// the default pbuffer surface.
if (!gl::GLSurfaceEGL::IsEGLFlexibleSurfaceCompatibilitySupported()) {
DLOG(ERROR) << "EGL_ANGLE_flexible_surface_compatibility not supported";
return false;
}
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
gl::QueryD3D11DeviceObjectFromANGLE();
if (!d3d11_device) {
DLOG(ERROR) << "Failed to retrieve D3D11 device";
return false;
}
// This will fail if DirectComposition DLL can't be loaded.
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device =
gl::QueryDirectCompositionDevice(d3d11_device);
if (!dcomp_device) {
DLOG(ERROR) << "Failed to retrieve direct composition device";
return false;
}
return true;
}();
return supported;
}
// static
bool DirectCompositionSurfaceWin::AreOverlaysSupported() {
// Always initialize and record overlay support information irrespective of // Always initialize and record overlay support information irrespective of
// command line flags. // command line flags.
InitializeHardwareOverlaySupport(); InitializeHardwareOverlaySupport();
...@@ -1528,7 +1504,6 @@ bool DirectCompositionSurfaceWin::AreOverlaysSupported() { ...@@ -1528,7 +1504,6 @@ bool DirectCompositionSurfaceWin::AreOverlaysSupported() {
// static // static
OverlayCapabilities DirectCompositionSurfaceWin::GetOverlayCapabilities() { OverlayCapabilities DirectCompositionSurfaceWin::GetOverlayCapabilities() {
InitializeHardwareOverlaySupport(); InitializeHardwareOverlaySupport();
OverlayCapabilities capabilities; OverlayCapabilities capabilities;
for (const auto& info : g_overlay_support_info) { for (const auto& info : g_overlay_support_info) {
if (info.flags) { if (info.flags) {
...@@ -1539,7 +1514,6 @@ OverlayCapabilities DirectCompositionSurfaceWin::GetOverlayCapabilities() { ...@@ -1539,7 +1514,6 @@ OverlayCapabilities DirectCompositionSurfaceWin::GetOverlayCapabilities() {
capabilities.push_back(cap); capabilities.push_back(cap);
} }
} }
return capabilities; return capabilities;
} }
......
...@@ -36,6 +36,12 @@ class GPU_IPC_SERVICE_EXPORT DirectCompositionSurfaceWin ...@@ -36,6 +36,12 @@ class GPU_IPC_SERVICE_EXPORT DirectCompositionSurfaceWin
base::WeakPtr<ImageTransportSurfaceDelegate> delegate, base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
HWND parent_window); HWND parent_window);
// Returns true if direct composition is supported. We prefer to use direct
// composition event without hardware overlays, because it allows us to bypass
// blitting by DWM to the window redirection surface by using a flip mode swap
// chain. Overridden with --disable-direct-composition.
static bool IsDirectCompositionSupported();
// Returns true if hardware overlays are supported, and DirectComposition // Returns true if hardware overlays are supported, and DirectComposition
// surface and layers should be used. Overridden with // surface and layers should be used. Overridden with
// --enable-direct-composition-layers and --disable-direct-composition-layers. // --enable-direct-composition-layers and --disable-direct-composition-layers.
......
...@@ -62,7 +62,9 @@ bool CollectGraphicsInfo(GPUInfo* gpu_info, ...@@ -62,7 +62,9 @@ bool CollectGraphicsInfo(GPUInfo* gpu_info,
#if defined(OS_WIN) #if defined(OS_WIN)
if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2) { if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2) {
gpu_info->direct_composition_overlays = gpu_info->direct_composition =
DirectCompositionSurfaceWin::IsDirectCompositionSupported();
gpu_info->supports_overlays =
DirectCompositionSurfaceWin::AreOverlaysSupported(); DirectCompositionSurfaceWin::AreOverlaysSupported();
gpu_info->overlay_capabilities = gpu_info->overlay_capabilities =
DirectCompositionSurfaceWin::GetOverlayCapabilities(); DirectCompositionSurfaceWin::GetOverlayCapabilities();
......
...@@ -34,7 +34,7 @@ scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface( ...@@ -34,7 +34,7 @@ scoped_refptr<gl::GLSurface> ImageTransportSurface::CreateNativeSurface(
auto vsync_provider = auto vsync_provider =
std::make_unique<gl::VSyncProviderWin>(surface_handle); std::make_unique<gl::VSyncProviderWin>(surface_handle);
if (DirectCompositionSurfaceWin::AreOverlaysSupported()) { if (DirectCompositionSurfaceWin::IsDirectCompositionSupported()) {
surface = base::MakeRefCounted<DirectCompositionSurfaceWin>( surface = base::MakeRefCounted<DirectCompositionSurfaceWin>(
std::move(vsync_provider), delegate, surface_handle); std::move(vsync_provider), delegate, surface_handle);
if (!surface->Initialize()) if (!surface->Initialize())
......
...@@ -98,6 +98,9 @@ const char kDisableGLExtensions[] = "disable-gl-extensions"; ...@@ -98,6 +98,9 @@ const char kDisableGLExtensions[] = "disable-gl-extensions";
// Enables SwapBuffersWithBounds if it is supported. // Enables SwapBuffersWithBounds if it is supported.
const char kEnableSwapBuffersWithBounds[] = "enable-swap-buffers-with-bounds"; const char kEnableSwapBuffersWithBounds[] = "enable-swap-buffers-with-bounds";
// Disables DirectComposition surface.
const char kDisableDirectComposition[] = "disable-direct-composition";
// Enables using DirectComposition layers, even if hardware overlays aren't // Enables using DirectComposition layers, even if hardware overlays aren't
// supported. // supported.
const char kEnableDirectCompositionLayers[] = const char kEnableDirectCompositionLayers[] =
...@@ -122,6 +125,7 @@ const char* const kGLSwitchesCopiedFromGpuProcessHost[] = { ...@@ -122,6 +125,7 @@ const char* const kGLSwitchesCopiedFromGpuProcessHost[] = {
kOverrideUseSoftwareGLForTests, kOverrideUseSoftwareGLForTests,
kUseANGLE, kUseANGLE,
kEnableSwapBuffersWithBounds, kEnableSwapBuffersWithBounds,
kDisableDirectComposition,
kEnableDirectCompositionLayers, kEnableDirectCompositionLayers,
kDisableDirectCompositionLayers, kDisableDirectCompositionLayers,
}; };
......
...@@ -54,6 +54,7 @@ GL_EXPORT extern const char kUseGpuInTests[]; ...@@ -54,6 +54,7 @@ GL_EXPORT extern const char kUseGpuInTests[];
GL_EXPORT extern const char kEnableSgiVideoSync[]; GL_EXPORT extern const char kEnableSgiVideoSync[];
GL_EXPORT extern const char kDisableGLExtensions[]; GL_EXPORT extern const char kDisableGLExtensions[];
GL_EXPORT extern const char kEnableSwapBuffersWithBounds[]; GL_EXPORT extern const char kEnableSwapBuffersWithBounds[];
GL_EXPORT extern const char kDisableDirectComposition[];
GL_EXPORT extern const char kEnableDirectCompositionLayers[]; GL_EXPORT extern const char kEnableDirectCompositionLayers[];
GL_EXPORT extern const char kDisableDirectCompositionLayers[]; GL_EXPORT extern const char kDisableDirectCompositionLayers[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment