Magic_Game/Kiwano/renderer/D3D10DeviceResources.cpp

440 lines
10 KiB
C++
Raw Normal View History

2019-04-11 14:40:54 +08:00
// Copyright (c) 2016-2018 Kiwano - Nomango
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "D3D10DeviceResources.h"
2019-04-11 14:40:54 +08:00
#if defined(KGE_USE_DIRECTX10)
#include "../2d/Image.h"
#include "../base/logs.h"
#pragma comment(lib, "d3d10_1.lib")
2019-04-11 14:40:54 +08:00
namespace kiwano
{
#if defined(_DEBUG)
namespace DX
{
HRESULT CreateD3DDevice(IDXGIAdapter *adapter, D3D10_DRIVER_TYPE driver_type, UINT flags, ID3D10Device1 **device)
{
HRESULT hr = S_OK;
static const D3D10_FEATURE_LEVEL1 levels[] =
{
D3D10_FEATURE_LEVEL_10_1,
D3D10_FEATURE_LEVEL_10_0,
D3D10_FEATURE_LEVEL_9_3,
D3D10_FEATURE_LEVEL_9_2,
D3D10_FEATURE_LEVEL_9_1,
};
for (UINT level = 0; level < ARRAYSIZE(levels); level++)
{
hr = D3D10CreateDevice1(
adapter,
driver_type,
NULL,
flags,
levels[level],
D3D10_1_SDK_VERSION,
device
);
if (SUCCEEDED(hr))
break;
}
return hr;
}
inline bool SdkLayersAvailable()
{
HRESULT hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
D3D10_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
nullptr // No need to keep the D3D device reference.
);
return SUCCEEDED(hr);
}
}
#endif
D3D10DeviceResources::D3D10DeviceResources()
: hwnd_(nullptr)
{
dpi_ = 96.f; // dpi_ = (float)GetDpiForWindow(hwnd);
}
D3D10DeviceResources::~D3D10DeviceResources()
{
DiscardResources();
}
HRESULT D3D10DeviceResources::Create(D3D10DeviceResources** device_resources, HWND hwnd)
{
HRESULT hr = E_FAIL;
if (device_resources)
{
D3D10DeviceResources* res = new (std::nothrow) D3D10DeviceResources;
if (res)
{
hr = res->CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
RECT rc;
GetClientRect(hwnd, &rc);
res->hwnd_ = hwnd;
res->logical_size_.x = float(rc.right - rc.left);
res->logical_size_.y = float(rc.bottom - rc.top);
hr = res->CreateDeviceResources();
}
if (SUCCEEDED(hr))
{
hr = res->CreateWindowSizeDependentResources();
}
if (SUCCEEDED(hr))
{
res->AddRef();
DX::SafeRelease(*device_resources);
(*device_resources) = res;
}
else
{
delete res;
res = nullptr;
}
}
}
return hr;
}
void D3D10DeviceResources::DiscardResources()
{
d3d_device_.Reset();
d3d_rt_view_.Reset();
d3d_ds_view_.Reset();
dxgi_swap_chain_.Reset();
dxgi_factory_.Reset();
hwnd_ = nullptr;
}
HRESULT D3D10DeviceResources::CreateDeviceResources()
{
HRESULT hr = S_OK;
// This flag adds support for surfaces with a different color channel ordering
// than the API default. It is required for compatibility with Direct2D.
UINT creation_flags = D3D10_CREATE_DEVICE_BGRA_SUPPORT;
2019-04-11 14:40:54 +08:00
#if defined(KGE_DEBUG)
if (DX::SdkLayersAvailable())
{
creation_flags |= D3D10_CREATE_DEVICE_DEBUG;
}
#endif
ComPtr<ID3D10Device1> device;
hr = DX::CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
creation_flags,
&device
);
if (SUCCEEDED(hr))
{
d3d_device_ = device;
ComPtr<IDXGIAdapter> dxgi_adapter;
ComPtr<IDXGIDevice> dxgi_device;
ComPtr<IDXGIFactory> dxgi_factory;
ComPtr<ID2D1Device> d2d_device;
if (SUCCEEDED(hr))
{
hr = d3d_device_->QueryInterface(IID_PPV_ARGS(&dxgi_device));
}
if (SUCCEEDED(hr))
{
hr = dxgi_device->GetAdapter(&dxgi_adapter);
}
if (SUCCEEDED(hr))
{
hr = dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));
}
if (SUCCEEDED(hr))
{
dxgi_factory_ = dxgi_factory;
}
// Create the Direct2D device object and a corresponding context.
if (SUCCEEDED(hr))
{
hr = GetD2DFactory()->CreateDevice(dxgi_device.Get(), &d2d_device);
}
if (SUCCEEDED(hr))
{
hr = SetD2DDevice(d2d_device);
}
}
if (SUCCEEDED(hr))
{
// Setup swap chain
DXGI_SWAP_CHAIN_DESC swap_chain_desc = { 0 };
swap_chain_desc.BufferCount = 2;
swap_chain_desc.BufferDesc.Width = ::lround(output_size_.x);
swap_chain_desc.BufferDesc.Height = ::lround(output_size_.y);
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swap_chain_desc.BufferDesc.RefreshRate.Numerator = 60;
swap_chain_desc.BufferDesc.RefreshRate.Denominator = 1;
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.OutputWindow = hwnd_;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.SampleDesc.Quality = 0;
swap_chain_desc.Windowed = TRUE;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
ComPtr<IDXGIDevice> dxgi_device;
if (SUCCEEDED(hr))
{
hr = d3d_device_->QueryInterface(&dxgi_device);
}
ComPtr<IDXGIAdapter> dxgi_adapter;
if (SUCCEEDED(hr))
{
hr = dxgi_device->GetAdapter(&dxgi_adapter);
}
ComPtr<IDXGIFactory> dxgi_factory;
if (SUCCEEDED(hr))
{
hr = dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));
}
if (SUCCEEDED(hr))
{
hr = dxgi_factory->CreateSwapChain(
d3d_device_.Get(),
&swap_chain_desc,
&dxgi_swap_chain_);
}
}
return hr;
}
HRESULT D3D10DeviceResources::CreateWindowSizeDependentResources()
{
if (!dxgi_swap_chain_)
return E_UNEXPECTED;
HRESULT hr = S_OK;
// Clear the previous window size specific context.
ID3D10RenderTargetView* null_views[] = { nullptr };
d3d_device_->OMSetRenderTargets(ARRAYSIZE(null_views), null_views, nullptr);
SetTargetBitmap(nullptr);
d3d_rt_view_ = nullptr;
d3d_ds_view_ = nullptr;
d3d_device_->Flush();
// Calculate the necessary render target size in pixels.
output_size_.x = DX::ConvertDipsToPixels(logical_size_.x, dpi_);
output_size_.y = DX::ConvertDipsToPixels(logical_size_.y, dpi_);
// Prevent zero size DirectX content from being created.
output_size_.x = std::max(output_size_.x, 1.f);
output_size_.y = std::max(output_size_.y, 1.f);
hr = dxgi_swap_chain_->ResizeBuffers(
2, // Double-buffered swap chain.
::lround(output_size_.x),
::lround(output_size_.y),
DXGI_FORMAT_UNKNOWN,
0);
if (SUCCEEDED(hr))
{
// Create the render target view and set it on the device
ComPtr<ID3D10Resource> dxgi_back_buffer;
hr = dxgi_swap_chain_->GetBuffer(
0,
IID_PPV_ARGS(&dxgi_back_buffer)
);
if (SUCCEEDED(hr))
{
D3D10_RENDER_TARGET_VIEW_DESC renderDesc;
renderDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
renderDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
renderDesc.Texture2D.MipSlice = 0;
d3d_rt_view_ = nullptr;
hr = d3d_device_->CreateRenderTargetView(dxgi_back_buffer.Get(), &renderDesc, &d3d_rt_view_);
}
}
if (SUCCEEDED(hr))
{
ComPtr<ID3D10Texture2D> depth_stencil;
D3D10_TEXTURE2D_DESC tex_desc;
tex_desc.ArraySize = 1;
tex_desc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
tex_desc.CPUAccessFlags = 0;
tex_desc.Format = DXGI_FORMAT_D16_UNORM;
tex_desc.Width = static_cast<UINT>(output_size_.x);
tex_desc.Height = static_cast<UINT>(output_size_.y);
tex_desc.MipLevels = 1;
tex_desc.MiscFlags = 0;
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
tex_desc.Usage = D3D10_USAGE_DEFAULT;
hr = d3d_device_->CreateTexture2D(&tex_desc, NULL, &depth_stencil);
if (SUCCEEDED(hr))
{
D3D10_DEPTH_STENCIL_VIEW_DESC desc;
desc.Format = DXGI_FORMAT_D16_UNORM;
desc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
desc.Texture2D.MipSlice = 0;
d3d_ds_view_.Reset();
hr = d3d_device_->CreateDepthStencilView(depth_stencil.Get(), &desc, &d3d_ds_view_);
}
if (SUCCEEDED(hr))
{
ID3D10RenderTargetView* main_view = d3d_rt_view_.Get();
d3d_device_context_->OMSetRenderTargets(1, &main_view, d3d_ds_view_.Get());
}
}
if (SUCCEEDED(hr))
{
// Set a new viewport based on the new dimensions
D3D10_VIEWPORT viewport;
viewport.Width = static_cast<UINT>(output_size_.x);
viewport.Height = static_cast<UINT>(output_size_.y);
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.MinDepth = 0;
viewport.MaxDepth = 1;
d3d_device_->RSSetViewports(1, &viewport);
}
// Create a Direct2D target bitmap associated with the
// swap chain back buffer and set it as the current target.
if (SUCCEEDED(hr))
{
ComPtr<IDXGISurface> dxgi_back_buffer;
hr = dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgi_back_buffer));
ComPtr<ID2D1Bitmap1> target;
if (SUCCEEDED(hr))
{
hr = GetD2DDeviceContext()->CreateBitmapFromDxgiSurface(
dxgi_back_buffer.Get(),
D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpi_,
dpi_),
&target);
}
if (SUCCEEDED(hr))
{
SetTargetBitmap(target);
}
}
return hr;
}
HRESULT D3D10DeviceResources::HandleDeviceLost()
{
dxgi_swap_chain_ = nullptr;
HRESULT hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
hr = CreateWindowSizeDependentResources();
}
return hr;
}
void D3D10DeviceResources::SetLogicalSize(Size logical_size)
{
if (logical_size_ != logical_size)
{
logical_size_ = logical_size;
ThrowIfFailed(
CreateWindowSizeDependentResources()
);
}
}
void D3D10DeviceResources::SetDpi(float dpi)
{
if (dpi != dpi_)
{
dpi_ = dpi;
RECT rc;
GetClientRect(hwnd_, &rc);
logical_size_.x = float(rc.right - rc.left);
logical_size_.y = float(rc.bottom - rc.top);
GetD2DDeviceContext()->SetDpi(dpi_, dpi_);
ThrowIfFailed(
CreateWindowSizeDependentResources()
);
}
}
}
2019-04-11 14:40:54 +08:00
#endif // !KGE_USE_DIRECTX10