blob: 04be1536228969739ca1791f7033cd6ae22c834d [file] [log] [blame]
// Copyright (c) 2017 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 "device/vr/windows/d3d11_texture_helper.h"
#include "mojo/public/c/system/platform_handle.h"
namespace {
#include "device/vr/windows/flip_pixel_shader.h"
#include "device/vr/windows/flip_vertex_shader.h"
constexpr int kAcquireWaitMS = 2000;
}
namespace device {
D3D11TextureHelper::RenderState::RenderState() {}
D3D11TextureHelper::RenderState::~RenderState() {}
D3D11TextureHelper::D3D11TextureHelper() {}
D3D11TextureHelper::~D3D11TextureHelper() {}
bool D3D11TextureHelper::CopyTextureToBackBuffer(bool flipY) {
if (!EnsureInitialized())
return false;
if (!render_state_.source_texture_)
return false;
if (!render_state_.target_texture_)
return false;
HRESULT hr = render_state_.keyed_mutex_->AcquireSync(1, kAcquireWaitMS);
if (FAILED(hr) || hr == WAIT_TIMEOUT || hr == WAIT_ABANDONED) {
// We failed to acquire the lock. We'll drop this frame, but subsequent
// frames won't be affected.
return false;
}
bool success = true;
if (flipY) {
success = CopyTextureWithFlip();
} else {
render_state_.d3d11_device_context_->CopyResource(
render_state_.target_texture_.Get(),
render_state_.source_texture_.Get());
}
render_state_.keyed_mutex_->ReleaseSync(0);
return success;
}
bool D3D11TextureHelper::EnsureRenderTargetView() {
if (!render_state_.render_target_view_) {
D3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc;
render_target_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
render_target_view_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
render_target_view_desc.Texture2D.MipSlice = 0;
HRESULT hr = render_state_.d3d11_device_->CreateRenderTargetView(
render_state_.target_texture_.Get(), &render_target_view_desc,
&render_state_.render_target_view_);
if (FAILED(hr))
return false;
}
return true;
}
bool D3D11TextureHelper::EnsureShaders() {
if (!render_state_.flip_vertex_shader_) {
HRESULT hr = render_state_.d3d11_device_->CreateVertexShader(
g_flip_vertex, _countof(g_flip_vertex), nullptr,
&render_state_.flip_vertex_shader_);
if (FAILED(hr))
return false;
}
if (!render_state_.flip_pixel_shader_) {
HRESULT hr = render_state_.d3d11_device_->CreatePixelShader(
g_flip_pixel, _countof(g_flip_pixel), nullptr,
&render_state_.flip_pixel_shader_);
if (FAILED(hr))
return false;
}
return true;
}
bool D3D11TextureHelper::EnsureInputLayout() {
if (!render_state_.input_layout_) {
D3D11_INPUT_ELEMENT_DESC vertex_desc = {
"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,
0, 0, D3D11_INPUT_PER_VERTEX_DATA,
0};
HRESULT hr = render_state_.d3d11_device_->CreateInputLayout(
&vertex_desc, 1, g_flip_vertex, _countof(g_flip_vertex),
&render_state_.input_layout_);
if (FAILED(hr))
return false;
}
return true;
}
bool D3D11TextureHelper::EnsureVertexBuffer() {
if (!render_state_.vertex_buffer_) {
// Pairs of x/y coordinates for 2 triangles in a quad.
float buffer_data[] = {-1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1};
D3D11_SUBRESOURCE_DATA vertex_buffer_data;
vertex_buffer_data.pSysMem = buffer_data;
vertex_buffer_data.SysMemPitch = 0;
vertex_buffer_data.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertex_buffer_desc(sizeof(buffer_data),
D3D11_BIND_VERTEX_BUFFER);
HRESULT hr = render_state_.d3d11_device_->CreateBuffer(
&vertex_buffer_desc, &vertex_buffer_data,
&render_state_.vertex_buffer_);
if (FAILED(hr))
return false;
}
return true;
}
bool D3D11TextureHelper::EnsureSampler() {
if (!render_state_.sampler_) {
CD3D11_DEFAULT default_values;
CD3D11_SAMPLER_DESC sampler_desc = CD3D11_SAMPLER_DESC(default_values);
D3D11_SAMPLER_DESC sd = sampler_desc;
HRESULT hr = render_state_.d3d11_device_->CreateSamplerState(
&sd, render_state_.sampler_.GetAddressOf());
if (FAILED(hr))
return false;
}
return true;
}
bool D3D11TextureHelper::CopyTextureWithFlip() {
if (!EnsureRenderTargetView() || !EnsureShaders() || !EnsureInputLayout() ||
!EnsureVertexBuffer() || !EnsureSampler())
return false;
render_state_.d3d11_device_context_->OMSetRenderTargets(
1, render_state_.render_target_view_.GetAddressOf(), nullptr);
render_state_.d3d11_device_context_->VSSetShader(
render_state_.flip_vertex_shader_.Get(), nullptr, 0);
render_state_.d3d11_device_context_->PSSetShader(
render_state_.flip_pixel_shader_.Get(), nullptr, 0);
render_state_.d3d11_device_context_->IASetInputLayout(
render_state_.input_layout_.Get());
UINT stride = 2 * sizeof(float);
UINT offset = 0;
render_state_.d3d11_device_context_->IASetVertexBuffers(
0, 1, render_state_.vertex_buffer_.GetAddressOf(), &stride, &offset);
render_state_.d3d11_device_context_->PSSetSamplers(
0, 1, render_state_.sampler_.GetAddressOf());
D3D11_SHADER_RESOURCE_VIEW_DESC shader_resource_view_desc;
shader_resource_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
shader_resource_view_desc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D;
shader_resource_view_desc.Texture2D.MostDetailedMip = 0;
shader_resource_view_desc.Texture2D.MipLevels = 1;
HRESULT hr = render_state_.d3d11_device_->CreateShaderResourceView(
render_state_.source_texture_.Get(), &shader_resource_view_desc,
render_state_.shader_resource_.ReleaseAndGetAddressOf());
if (FAILED(hr))
return false;
render_state_.d3d11_device_context_->PSSetShaderResources(
0, 1, render_state_.shader_resource_.GetAddressOf());
D3D11_TEXTURE2D_DESC desc;
render_state_.target_texture_->GetDesc(&desc);
D3D11_VIEWPORT viewport = {0, 0, desc.Width, desc.Height, 0, 1};
render_state_.d3d11_device_context_->RSSetViewports(1, &viewport);
render_state_.d3d11_device_context_->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
render_state_.d3d11_device_context_->Draw(6, 0);
return true;
}
void D3D11TextureHelper::SetSourceTexture(
base::win::ScopedHandle texture_handle) {
render_state_.source_texture_ = nullptr;
render_state_.keyed_mutex_ = nullptr;
if (!EnsureInitialized())
return;
texture_handle_ = std::move(texture_handle);
HRESULT hr = render_state_.d3d11_device_->OpenSharedResource1(
texture_handle_.Get(),
IID_PPV_ARGS(render_state_.keyed_mutex_.ReleaseAndGetAddressOf()));
if (FAILED(hr))
return;
hr = render_state_.keyed_mutex_.CopyTo(
render_state_.source_texture_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
render_state_.keyed_mutex_ = nullptr;
}
}
void D3D11TextureHelper::AllocateBackBuffer() {
if (!EnsureInitialized())
return;
if (!render_state_.source_texture_)
return;
D3D11_TEXTURE2D_DESC desc_source;
render_state_.source_texture_->GetDesc(&desc_source);
desc_source.MiscFlags = 0;
if (render_state_.target_texture_) {
D3D11_TEXTURE2D_DESC desc_target;
render_state_.target_texture_->GetDesc(&desc_target);
// If the target should change size, format, or other properties reallocate
// a new target.
if (desc_source.Width != desc_target.Width ||
desc_source.Height != desc_target.Height ||
desc_source.MipLevels != desc_target.MipLevels ||
desc_source.ArraySize != desc_target.ArraySize ||
desc_source.Format != desc_target.Format ||
desc_source.SampleDesc.Count != desc_target.SampleDesc.Count ||
desc_source.SampleDesc.Quality != desc_target.SampleDesc.Quality ||
desc_source.Usage != desc_target.Usage ||
desc_source.BindFlags != desc_target.BindFlags ||
desc_source.CPUAccessFlags != desc_target.CPUAccessFlags ||
desc_source.MiscFlags != desc_target.MiscFlags) {
render_state_.target_texture_ = nullptr;
}
}
if (!render_state_.target_texture_) {
// Ignoring error - target_texture_ will be null on failure.
render_state_.d3d11_device_->CreateTexture2D(
&desc_source, nullptr,
render_state_.target_texture_.ReleaseAndGetAddressOf());
}
}
const Microsoft::WRL::ComPtr<ID3D11Texture2D>&
D3D11TextureHelper::GetBackbuffer() {
return render_state_.target_texture_;
}
void D3D11TextureHelper::SetBackbuffer(
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer) {
if (render_state_.target_texture_ != back_buffer) {
render_state_.render_target_view_ = nullptr;
}
render_state_.target_texture_ = back_buffer;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> D3D11TextureHelper::GetAdapter() {
Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory;
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
if (FAILED(hr))
return nullptr;
if (adapter_index_ >= 0) {
dxgi_factory->EnumAdapters(adapter_index_, adapter.GetAddressOf());
} else {
// We don't have a valid adapter index, lets see if we have a valid LUID.
Microsoft::WRL::ComPtr<IDXGIFactory4> dxgi_factory4;
hr = dxgi_factory.As(&dxgi_factory4);
if (FAILED(hr))
return nullptr;
dxgi_factory4->EnumAdapterByLuid(adapter_luid_,
IID_PPV_ARGS(adapter.GetAddressOf()));
}
return adapter;
}
Microsoft::WRL::ComPtr<ID3D11Device> D3D11TextureHelper::GetDevice() {
EnsureInitialized();
return render_state_.d3d11_device_;
}
bool D3D11TextureHelper::EnsureInitialized() {
if (render_state_.d3d11_device_ &&
SUCCEEDED(render_state_.d3d11_device_->GetDeviceRemovedReason()))
return true; // Already initialized.
// If we were previously initialized, but lost the device, throw away old
// state. This will be initialized lazily as needed.
render_state_ = {};
D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1};
UINT flags = 0;
D3D_FEATURE_LEVEL feature_level_out = D3D_FEATURE_LEVEL_11_1;
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter = GetAdapter();
if (!adapter) {
return false;
}
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device;
HRESULT hr = D3D11CreateDevice(
adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, feature_levels,
arraysize(feature_levels), D3D11_SDK_VERSION, d3d11_device.GetAddressOf(),
&feature_level_out, render_state_.d3d11_device_context_.GetAddressOf());
if (SUCCEEDED(hr)) {
hr = d3d11_device.As(&render_state_.d3d11_device_);
if (FAILED(hr)) {
render_state_.d3d11_device_context_ = nullptr;
}
}
return SUCCEEDED(hr);
}
bool D3D11TextureHelper::SetAdapterIndex(int32_t index) {
adapter_index_ = index;
return (index >= 0);
}
bool D3D11TextureHelper::SetAdapterLUID(const LUID& luid) {
adapter_luid_ = luid;
adapter_index_ = -1;
return true;
}
} // namespace device