blob: 68034391cd7b65268835044045e16487db148903 [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 "base/stl_util.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "mojo/public/c/system/platform_handle.h"
namespace {
#include "device/vr/windows/flip_pixel_shader.h"
#include "device/vr/windows/geometry_shader.h"
#include "device/vr/windows/vertex_shader.h"
constexpr int kAcquireWaitMS = 2000;
struct Vertex2D {
float x;
float y;
float u;
float v;
// Which texture in a texture array to output this triangle to? If we only
// have a single texture bound as the render target, this is ignored.
int target;
};
constexpr size_t kSizeOfVertex = sizeof(Vertex2D);
// 2 triangles per eye
constexpr size_t kNumVerticesPerLayer = 12;
}
namespace {
// This enum is used in TRACE_EVENTs. Try to keep enum values the same to make
// analysis easier across builds.
enum ErrorLocation {
OverlayBlendState = 1,
ContentBlendState = 2,
SourceTimeout = 3,
OverlayTimeout = 4,
BindTarget = 5,
EnsureRenderTargetView = 6,
EnsureRenderTargetView2 = 7,
EnsureVS = 8,
EnsureGS = 9,
EnsurePS = 10,
InputLayout = 11,
VertexBuffer = 12,
Sampler = 13,
ShaderResource = 14,
OpenSource = 15,
OpenOverlay = 16,
CreateDevice = 17,
};
void TraceDXError(ErrorLocation location, HRESULT hr) {
TRACE_EVENT_INSTANT2("xr", "TraceDXError", TRACE_EVENT_SCOPE_THREAD,
"ErrorLocation", location, "hr", hr);
}
} // namespace
namespace device {
D3D11TextureHelper::RenderState::RenderState() {}
D3D11TextureHelper::RenderState::~RenderState() {}
D3D11TextureHelper::LayerData::LayerData() = default;
D3D11TextureHelper::LayerData::~LayerData() = default;
D3D11TextureHelper::D3D11TextureHelper() {}
D3D11TextureHelper::~D3D11TextureHelper() {}
void D3D11TextureHelper::Reset() {
render_state_ = {};
}
void D3D11TextureHelper::SetSourceAndOverlayVisible(bool source_visible,
bool overlay_visible) {
source_visible_ = source_visible;
overlay_visible_ = overlay_visible;
TRACE_EVENT_INSTANT2("xr", "TextureHelper SetSourceAndOverlayVisible",
TRACE_EVENT_SCOPE_THREAD, "source", source_visible,
"overlay", overlay_visible);
if (!source_visible_) {
render_state_.source_.keyed_mutex_ = nullptr;
render_state_.source_.source_texture_ = nullptr;
render_state_.source_.shader_resource_ = nullptr;
render_state_.source_.sampler_ = nullptr;
}
if (!overlay_visible_) {
render_state_.overlay_.keyed_mutex_ = nullptr;
render_state_.overlay_.source_texture_ = nullptr;
render_state_.overlay_.shader_resource_ = nullptr;
render_state_.overlay_.sampler_ = nullptr;
}
}
void D3D11TextureHelper::CleanupNoSubmit() {
render_state_.source_.keyed_mutex_ = nullptr;
render_state_.source_.source_texture_ = nullptr;
render_state_.source_.shader_resource_ = nullptr;
render_state_.source_.sampler_ = nullptr;
render_state_.overlay_.keyed_mutex_ = nullptr;
render_state_.overlay_.source_texture_ = nullptr;
render_state_.overlay_.shader_resource_ = nullptr;
render_state_.overlay_.sampler_ = nullptr;
}
void D3D11TextureHelper::CleanupLayerData(LayerData& layer) {
if (!layer.submitted_this_frame_) {
layer.keyed_mutex_ = nullptr;
layer.sampler_ = nullptr;
layer.shader_resource_ = nullptr;
layer.source_texture_ = nullptr;
}
// Set up for the next frame so we know if we submitted again.
layer.submitted_this_frame_ = false;
}
bool D3D11TextureHelper::EnsureOverlayBlendState() {
if (!render_state_.overlay_blend_state_) {
D3D11_BLEND_DESC blenddesc = {};
blenddesc.RenderTarget[0].BlendEnable = true;
blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blenddesc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
HRESULT hr = render_state_.d3d11_device_->CreateBlendState(
&blenddesc,
render_state_.overlay_blend_state_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
TraceDXError(ErrorLocation::OverlayBlendState, hr);
return false;
}
}
if (render_state_.overlay_blend_state_ !=
render_state_.current_blend_state_) {
render_state_.d3d11_device_context_->OMSetBlendState(
render_state_.overlay_blend_state_.Get(), 0, -1);
render_state_.current_blend_state_ = render_state_.overlay_blend_state_;
}
return true;
}
bool D3D11TextureHelper::EnsureContentBlendState() {
if (!render_state_.content_blend_state_) {
D3D11_BLEND_DESC blenddesc = {};
blenddesc.RenderTarget[0].BlendEnable = false;
blenddesc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
HRESULT hr = render_state_.d3d11_device_->CreateBlendState(
&blenddesc,
render_state_.content_blend_state_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
TraceDXError(ErrorLocation::ContentBlendState, hr);
return false;
}
}
if (render_state_.content_blend_state_ !=
render_state_.current_blend_state_) {
render_state_.d3d11_device_context_->OMSetBlendState(
render_state_.content_blend_state_.Get(), 0, -1);
render_state_.current_blend_state_ = render_state_.content_blend_state_;
}
return true;
}
bool D3D11TextureHelper::CompositeToBackBuffer() {
if (!EnsureInitialized())
return false;
// Clear stale data:
CleanupLayerData(render_state_.source_);
CleanupLayerData(render_state_.overlay_);
if (!render_state_.source_.source_texture_ &&
!render_state_.overlay_.source_texture_)
return false;
if (!render_state_.target_texture_)
return false;
HRESULT hr = S_OK;
if (render_state_.source_.keyed_mutex_) {
hr = render_state_.source_.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.
TraceDXError(ErrorLocation::SourceTimeout, hr);
return false;
}
}
if (render_state_.overlay_.keyed_mutex_) {
hr = render_state_.overlay_.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.
TraceDXError(ErrorLocation::OverlayTimeout, hr);
if (render_state_.source_.keyed_mutex_) {
render_state_.source_.keyed_mutex_->ReleaseSync(0);
}
return false;
}
}
if (!BindTarget()) {
TraceDXError(ErrorLocation::BindTarget, hr);
return false;
}
if (render_state_.overlay_.source_texture_ &&
(!render_state_.source_.source_texture_ || !source_visible_)) {
// If we have an overlay, but no WebXR texture under it, clear the target
// first, since overlay may assume transparency.
float color_rgba[4] = {0, 0, 0, 1};
render_state_.d3d11_device_context_->ClearRenderTargetView(
render_state_.render_target_view_.Get(), color_rgba);
}
bool success = true;
if (render_state_.source_.source_texture_)
success = success && EnsureContentBlendState() &&
CompositeLayer(render_state_.source_);
if (render_state_.overlay_.source_texture_)
success = success && EnsureOverlayBlendState() &&
CompositeLayer(render_state_.overlay_);
if (render_state_.source_.keyed_mutex_)
render_state_.source_.keyed_mutex_->ReleaseSync(0);
if (render_state_.overlay_.keyed_mutex_)
render_state_.overlay_.keyed_mutex_->ReleaseSync(0);
return success;
}
bool D3D11TextureHelper::EnsureRenderTargetView() {
if (!render_state_.render_target_view_) {
D3D11_TEXTURE2D_DESC desc;
render_state_.target_texture_->GetDesc(&desc);
if (desc.ArraySize > 1) {
HRESULT hr = render_state_.d3d11_device_->CreateRenderTargetView(
render_state_.target_texture_.Get(), nullptr,
&render_state_.render_target_view_);
if (FAILED(hr)) {
TraceDXError(ErrorLocation::EnsureRenderTargetView, hr);
}
return SUCCEEDED(hr);
}
D3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc;
// If the resource is unknown or typeless, use R8G8B8A8_UNORM, which is
// required for Oculus. Otherwise, use the resource's native type.
render_target_view_desc.Format =
(desc.Format == DXGI_FORMAT_UNKNOWN ||
desc.Format == DXGI_FORMAT_R8G8B8A8_TYPELESS)
? DXGI_FORMAT_R8G8B8A8_UNORM
: DXGI_FORMAT_UNKNOWN;
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)) {
TraceDXError(ErrorLocation::EnsureRenderTargetView2, hr);
return false;
}
}
return true;
}
bool D3D11TextureHelper::EnsureShaders() {
if (!render_state_.vertex_shader_) {
HRESULT hr = render_state_.d3d11_device_->CreateVertexShader(
g_vertex, _countof(g_vertex), nullptr, &render_state_.vertex_shader_);
if (FAILED(hr)) {
TraceDXError(ErrorLocation::EnsureVS, hr);
return false;
}
}
if (!render_state_.geometry_shader_) {
HRESULT hr = render_state_.d3d11_device_->CreateGeometryShader(
g_geometry, _countof(g_geometry), nullptr,
&render_state_.geometry_shader_);
if (FAILED(hr)) {
TraceDXError(ErrorLocation::EnsureGS, 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)) {
TraceDXError(ErrorLocation::EnsurePS, 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},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0,
D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 1, DXGI_FORMAT_R32_UINT, 0, D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA, 0},
};
HRESULT hr = render_state_.d3d11_device_->CreateInputLayout(
vertex_desc, base::size(vertex_desc), g_vertex, _countof(g_vertex),
&render_state_.input_layout_);
if (FAILED(hr)) {
TraceDXError(ErrorLocation::InputLayout, hr);
return false;
}
}
return true;
}
bool D3D11TextureHelper::EnsureVertexBuffer() {
if (!render_state_.vertex_buffer_) {
// Pairs of x/y coordinates, and UVs for 2 triangles in a quad.
CD3D11_BUFFER_DESC vertex_buffer_desc(kSizeOfVertex * kNumVerticesPerLayer,
D3D11_BIND_VERTEX_BUFFER);
HRESULT hr = render_state_.d3d11_device_->CreateBuffer(
&vertex_buffer_desc, nullptr, &render_state_.vertex_buffer_);
if (FAILED(hr)) {
TraceDXError(ErrorLocation::VertexBuffer, hr);
return false;
}
}
return true;
}
bool D3D11TextureHelper::EnsureSampler(LayerData& layer) {
if (!layer.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, layer.sampler_.GetAddressOf());
if (FAILED(hr)) {
TraceDXError(ErrorLocation::Sampler, hr);
return false;
}
}
return true;
}
bool D3D11TextureHelper::BindTarget() {
if (!EnsureRenderTargetView())
return false;
render_state_.d3d11_device_context_->OMSetRenderTargets(
1, render_state_.render_target_view_.GetAddressOf(), nullptr);
return true;
}
void PushVertRect(std::vector<Vertex2D>& data,
const gfx::RectF& rect,
const gfx::RectF& uv,
int target) {
Vertex2D vert;
vert.target = target;
vert.x = rect.x() * 2 - 1;
vert.y = rect.y() * 2 - 1;
vert.u = uv.x();
vert.v = uv.y();
data.push_back(vert);
vert.x = rect.x() * 2 - 1;
vert.y = (rect.y() + rect.height()) * 2 - 1;
vert.u = uv.x();
vert.v = uv.y() + uv.height();
data.push_back(vert);
vert.x = (rect.x() + rect.width()) * 2 - 1;
vert.y = (rect.y() + rect.height()) * 2 - 1;
vert.u = uv.x() + uv.width();
vert.v = uv.y() + uv.height();
data.push_back(vert);
vert.x = rect.x() * 2 - 1;
vert.y = rect.y() * 2 - 1;
vert.u = uv.x();
vert.v = uv.y();
data.push_back(vert);
vert.x = (rect.x() + rect.width()) * 2 - 1;
vert.y = (rect.y() + rect.height()) * 2 - 1;
vert.u = uv.x() + uv.width();
vert.v = uv.y() + uv.height();
data.push_back(vert);
vert.x = (rect.x() + rect.width()) * 2 - 1;
vert.y = rect.y() * 2 - 1;
vert.u = uv.x() + uv.width();
vert.v = uv.y();
data.push_back(vert);
}
bool D3D11TextureHelper::UpdateVertexBuffer(LayerData& layer) {
std::vector<Vertex2D> vertex_data;
PushVertRect(vertex_data, target_left_, layer.left_, 0);
PushVertRect(vertex_data, target_right_, layer.right_, 1);
render_state_.d3d11_device_context_->UpdateSubresource(
render_state_.vertex_buffer_.Get(), 0, nullptr, vertex_data.data(),
sizeof(Vertex2D), vertex_data.size());
return true;
}
bool D3D11TextureHelper::CompositeLayer(LayerData& layer) {
if (!EnsureShaders() || !EnsureInputLayout() || !EnsureVertexBuffer() ||
!EnsureSampler(layer) || !UpdateVertexBuffer(layer))
return false;
render_state_.d3d11_device_context_->VSSetShader(
render_state_.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 = kSizeOfVertex;
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, layer.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(
layer.source_texture_.Get(), &shader_resource_view_desc,
layer.shader_resource_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
TraceDXError(ErrorLocation::ShaderResource, hr);
return false;
}
render_state_.d3d11_device_context_->PSSetShaderResources(
0, 1, layer.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);
// TODO(billorr): Optimize to avoid the geometry shader when not needed.
render_state_.d3d11_device_context_->GSSetShader(
render_state_.geometry_shader_.Get(), nullptr, 0);
render_state_.d3d11_device_context_->Draw(kNumVerticesPerLayer, 0);
return true;
}
bool D3D11TextureHelper::SetSourceTexture(
base::win::ScopedHandle texture_handle,
gfx::RectF left,
gfx::RectF right) {
TRACE_EVENT0("xr", "SetSourceTexture");
render_state_.source_.source_texture_ = nullptr;
render_state_.source_.keyed_mutex_ = nullptr;
render_state_.source_.left_ = left;
render_state_.source_.right_ = right;
render_state_.source_.submitted_this_frame_ = true;
if (!EnsureInitialized())
return false;
HRESULT hr = render_state_.d3d11_device_->OpenSharedResource1(
texture_handle.Get(),
IID_PPV_ARGS(
render_state_.source_.keyed_mutex_.ReleaseAndGetAddressOf()));
if (FAILED(hr)) {
TraceDXError(ErrorLocation::OpenSource, hr);
return false;
}
hr = render_state_.source_.keyed_mutex_.CopyTo(
render_state_.source_.source_texture_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
render_state_.source_.keyed_mutex_ = nullptr;
return false;
}
return true;
}
bool D3D11TextureHelper::SetOverlayTexture(
base::win::ScopedHandle texture_handle,
gfx::RectF left,
gfx::RectF right) {
render_state_.overlay_.source_texture_ = nullptr;
render_state_.overlay_.keyed_mutex_ = nullptr;
render_state_.overlay_.left_ = left;
render_state_.overlay_.right_ = right;
render_state_.overlay_.submitted_this_frame_ = true;
if (!EnsureInitialized())
return false;
HRESULT hr = render_state_.d3d11_device_->OpenSharedResource1(
texture_handle.Get(),
IID_PPV_ARGS(
render_state_.overlay_.keyed_mutex_.ReleaseAndGetAddressOf()));
if (FAILED(hr)) {
TraceDXError(ErrorLocation::OpenOverlay, hr);
return false;
}
hr = render_state_.overlay_.keyed_mutex_.CopyTo(
render_state_.overlay_.source_texture_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
render_state_.overlay_.keyed_mutex_ = nullptr;
return false;
}
return true;
}
bool D3D11TextureHelper::UpdateBackbufferSizes() {
if (!EnsureInitialized())
return false;
if (!render_state_.source_.source_texture_ &&
!render_state_.overlay_.source_texture_)
return false;
if (force_viewport_) {
target_size_ = default_size_;
return true;
}
if (render_state_.source_.source_texture_ &&
render_state_.overlay_.source_texture_) {
target_left_ = gfx::RectF(0 /*x*/, 0 /*y*/, 0.5f /*width*/, 1 /*height*/);
target_right_ =
gfx::RectF(0.5f /*x*/, 0 /*y*/, 0.5f /*width*/, 1 /*height*/);
target_size_ = default_size_;
return true;
}
LayerData* layer = render_state_.overlay_.source_texture_
? &render_state_.overlay_
: &render_state_.source_;
D3D11_TEXTURE2D_DESC desc_desired;
layer->source_texture_->GetDesc(&desc_desired);
target_left_ = layer->left_;
target_right_ = layer->right_;
target_size_ = gfx::Size(desc_desired.Width, desc_desired.Height);
return true;
}
void D3D11TextureHelper::AllocateBackBuffer() {
if (!EnsureInitialized())
return;
// If we don't have anything to composite, just return.
if (!render_state_.source_.source_texture_ &&
!render_state_.overlay_.source_texture_)
return;
LayerData* layer = render_state_.overlay_.source_texture_
? &render_state_.overlay_
: &render_state_.source_;
D3D11_TEXTURE2D_DESC desc_desired;
layer->source_texture_->GetDesc(&desc_desired);
desc_desired.MiscFlags = 0;
desc_desired.Width = target_size_.width();
desc_desired.Height = target_size_.height();
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 texture and new render target view.
if (desc_desired.Width != desc_target.Width ||
desc_desired.Height != desc_target.Height ||
desc_desired.MipLevels != desc_target.MipLevels ||
desc_desired.ArraySize != desc_target.ArraySize ||
desc_desired.Format != desc_target.Format ||
desc_desired.SampleDesc.Count != desc_target.SampleDesc.Count ||
desc_desired.SampleDesc.Quality != desc_target.SampleDesc.Quality ||
desc_desired.Usage != desc_target.Usage ||
desc_desired.BindFlags != desc_target.BindFlags ||
desc_desired.CPUAccessFlags != desc_target.CPUAccessFlags ||
desc_desired.MiscFlags != desc_target.MiscFlags) {
render_state_.target_texture_ = nullptr;
render_state_.render_target_view_ = nullptr;
}
}
if (!render_state_.target_texture_) {
// Ignoring error - target_texture_ will be null on failure.
render_state_.d3d11_device_->CreateTexture2D(
&desc_desired, nullptr,
render_state_.target_texture_.ReleaseAndGetAddressOf());
}
}
const Microsoft::WRL::ComPtr<ID3D11Texture2D>&
D3D11TextureHelper::GetBackbuffer() {
return render_state_.target_texture_;
}
void D3D11TextureHelper::DiscardView() {
if (render_state_.render_target_view_ &&
render_state_.d3d11_device_context_) {
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> context1;
if (SUCCEEDED(render_state_.d3d11_device_context_.As(&context1))) {
context1->DiscardView(render_state_.render_target_view_.Get());
}
}
}
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 = bgra_ ? D3D11_CREATE_DEVICE_BGRA_SUPPORT : 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,
base::size(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;
}
}
TraceDXError(ErrorLocation::CreateDevice, hr);
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