2017-10-11 16:22:13 +02:00
|
|
|
#include "dxgi_factory.h"
|
2017-11-29 15:16:07 +01:00
|
|
|
#include "dxgi_resource.h"
|
2017-10-11 16:22:13 +02:00
|
|
|
#include "dxgi_swapchain.h"
|
|
|
|
|
|
|
|
namespace dxvk {
|
|
|
|
|
|
|
|
DxgiSwapChain::DxgiSwapChain(
|
|
|
|
DxgiFactory* factory,
|
|
|
|
IUnknown* pDevice,
|
2017-11-26 14:02:08 +01:00
|
|
|
DXGI_SWAP_CHAIN_DESC* pDesc)
|
|
|
|
: m_factory (factory),
|
|
|
|
m_desc (*pDesc) {
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
// Retrieve a device pointer that allows us to
|
|
|
|
// communicate with the underlying D3D device
|
2017-11-29 16:23:33 +01:00
|
|
|
if (FAILED(pDevice->QueryInterface(__uuidof(IDXGIPresentDevicePrivate),
|
2017-12-04 11:33:04 +01:00
|
|
|
reinterpret_cast<void**>(&m_presentDevice))))
|
2017-11-26 14:02:08 +01:00
|
|
|
throw DxvkError("DxgiSwapChain::DxgiSwapChain: Invalid device");
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
// Retrieve the adapter, which is going
|
|
|
|
// to be used to enumerate displays.
|
2017-12-04 11:33:04 +01:00
|
|
|
Com<IDXGIAdapter> adapter;
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2017-12-04 11:33:04 +01:00
|
|
|
if (FAILED(pDevice->QueryInterface(__uuidof(IDXGIDevicePrivate),
|
|
|
|
reinterpret_cast<void**>(&m_device))))
|
2017-11-29 07:55:44 +01:00
|
|
|
throw DxvkError("DxgiSwapChain::DxgiSwapChain: Invalid device");
|
|
|
|
|
2017-12-04 11:33:04 +01:00
|
|
|
if (FAILED(m_device->GetAdapter(&adapter))
|
|
|
|
|| FAILED(adapter->QueryInterface(__uuidof(IDXGIAdapterPrivate),
|
|
|
|
reinterpret_cast<void**>(&m_adapter))))
|
2017-11-29 07:55:44 +01:00
|
|
|
throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to retrieve adapter");
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
// Initialize frame statistics
|
|
|
|
m_stats.PresentCount = 0;
|
|
|
|
m_stats.PresentRefreshCount = 0;
|
|
|
|
m_stats.SyncRefreshCount = 0;
|
|
|
|
m_stats.SyncQPCTime.QuadPart = 0;
|
|
|
|
m_stats.SyncGPUTime.QuadPart = 0;
|
|
|
|
|
2017-12-04 22:21:02 +01:00
|
|
|
// Adjust initial back buffer size. If zero, these
|
|
|
|
// shall be set to the current window size.
|
|
|
|
VkExtent2D windowSize = this->getWindowSize();
|
|
|
|
|
|
|
|
if (m_desc.BufferDesc.Width == 0) m_desc.BufferDesc.Width = windowSize.width;
|
|
|
|
if (m_desc.BufferDesc.Height == 0) m_desc.BufferDesc.Height = windowSize.height;
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
// Set initial window mode and fullscreen state
|
2017-12-05 14:43:03 +01:00
|
|
|
// if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr)))
|
|
|
|
// throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial fullscreen state");
|
2017-11-26 18:38:50 +01:00
|
|
|
|
2017-11-29 21:46:09 +01:00
|
|
|
this->createPresenter();
|
2017-11-29 07:55:44 +01:00
|
|
|
this->createBackBuffer();
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxgiSwapChain::~DxgiSwapChain() {
|
2017-11-26 15:29:57 +01:00
|
|
|
// We do not release the SDL window handle here since
|
|
|
|
// that would destroy the underlying window as well.
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::QueryInterface(REFIID riid, void** ppvObject) {
|
2017-10-15 21:50:45 +02:00
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IUnknown);
|
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IDXGIObject);
|
2017-11-26 14:02:08 +01:00
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IDXGIDeviceSubObject);
|
2017-10-11 16:22:13 +02:00
|
|
|
COM_QUERY_IFACE(riid, ppvObject, IDXGISwapChain);
|
|
|
|
|
|
|
|
Logger::warn("DxgiSwapChain::QueryInterface: Unknown interface query");
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetParent(REFIID riid, void** ppParent) {
|
2017-11-26 14:02:08 +01:00
|
|
|
return m_factory->QueryInterface(riid, ppParent);
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetDevice(REFIID riid, void** ppDevice) {
|
2017-11-26 14:02:08 +01:00
|
|
|
return m_device->QueryInterface(riid, ppDevice);
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) {
|
2017-11-26 15:29:57 +01:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
|
2017-11-26 18:38:50 +01:00
|
|
|
if (Buffer > 0) {
|
|
|
|
Logger::err("DxgiSwapChain::GetBuffer: Buffer > 0 not supported");
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
return m_backBufferIface->QueryInterface(riid, ppSurface);
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetContainingOutput(IDXGIOutput** ppOutput) {
|
2017-11-26 15:29:57 +01:00
|
|
|
if (ppOutput != nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
2017-12-05 14:43:03 +01:00
|
|
|
Logger::err("DxgiSwapChain::GetContainingOutput: Not implemented yet");
|
|
|
|
return E_NOTIMPL;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) {
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pDesc == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
*pDesc = m_desc;
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pStats == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
*pStats = m_stats;
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetFullscreenState(
|
|
|
|
BOOL* pFullscreen,
|
|
|
|
IDXGIOutput** ppTarget) {
|
2017-11-26 15:29:57 +01:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
if (pFullscreen != nullptr)
|
|
|
|
*pFullscreen = !m_desc.Windowed;
|
|
|
|
|
|
|
|
if ((ppTarget != nullptr) && !m_desc.Windowed)
|
|
|
|
hr = this->GetContainingOutput(ppTarget);
|
|
|
|
|
|
|
|
return hr;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) {
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pLastPresentCount == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
*pLastPresentCount = m_stats.PresentCount;
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {
|
2017-11-26 15:29:57 +01:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
try {
|
|
|
|
// Submit pending rendering commands
|
|
|
|
// before recording the present code.
|
2017-12-04 11:33:04 +01:00
|
|
|
m_presentDevice->FlushRenderingCommands();
|
2017-11-26 18:38:50 +01:00
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
// TODO implement sync interval
|
|
|
|
// TODO implement flags
|
2017-11-29 21:46:09 +01:00
|
|
|
m_presenter->presentImage(m_backBufferView);
|
2017-11-29 07:55:44 +01:00
|
|
|
return S_OK;
|
|
|
|
} catch (const DxvkError& err) {
|
|
|
|
Logger::err(err.message());
|
|
|
|
return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
|
|
|
|
}
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::ResizeBuffers(
|
|
|
|
UINT BufferCount,
|
|
|
|
UINT Width,
|
|
|
|
UINT Height,
|
|
|
|
DXGI_FORMAT NewFormat,
|
|
|
|
UINT SwapChainFlags) {
|
2017-11-26 15:29:57 +01:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
|
2017-12-04 22:21:02 +01:00
|
|
|
VkExtent2D windowSize = this->getWindowSize();
|
|
|
|
|
|
|
|
m_desc.BufferDesc.Width = Width != 0 ? Width : windowSize.width;
|
|
|
|
m_desc.BufferDesc.Height = Height != 0 ? Height : windowSize.height;
|
|
|
|
|
|
|
|
m_desc.Flags = SwapChainFlags;
|
2017-11-26 18:38:50 +01:00
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
if (BufferCount != 0)
|
2017-12-04 22:21:02 +01:00
|
|
|
m_desc.BufferCount = BufferCount;
|
|
|
|
|
|
|
|
if (NewFormat != DXGI_FORMAT_UNKNOWN)
|
|
|
|
m_desc.BufferDesc.Format = NewFormat;
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2017-11-26 18:38:50 +01:00
|
|
|
try {
|
2017-12-04 22:21:02 +01:00
|
|
|
m_presenter->recreateSwapchain(
|
|
|
|
m_desc.BufferDesc.Width,
|
|
|
|
m_desc.BufferDesc.Height,
|
|
|
|
m_desc.BufferDesc.Format);
|
2017-11-29 08:29:12 +01:00
|
|
|
this->createBackBuffer();
|
2017-11-26 18:38:50 +01:00
|
|
|
return S_OK;
|
|
|
|
} catch (const DxvkError& err) {
|
|
|
|
Logger::err(err.message());
|
|
|
|
return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
|
|
|
|
}
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) {
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pNewTargetParameters == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
|
|
|
|
// Applies to windowed mode
|
2017-12-05 14:43:03 +01:00
|
|
|
RECT newRect;
|
|
|
|
RECT oldRect;
|
|
|
|
|
|
|
|
::SetRect(&newRect, 0, 0,
|
2017-11-26 15:29:57 +01:00
|
|
|
pNewTargetParameters->Width,
|
|
|
|
pNewTargetParameters->Height);
|
2017-12-05 14:43:03 +01:00
|
|
|
::AdjustWindowRectEx(&newRect,
|
|
|
|
GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE,
|
|
|
|
GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE));
|
|
|
|
::SetRect(&newRect, 0, 0,
|
|
|
|
newRect.right - newRect.left,
|
|
|
|
newRect.bottom - newRect.top);
|
|
|
|
::GetWindowRect(m_desc.OutputWindow, &oldRect);
|
|
|
|
::OffsetRect(&newRect, oldRect.left, oldRect.top);
|
|
|
|
|
|
|
|
// TODO implement fullscreen mode
|
|
|
|
|
|
|
|
::MoveWindow(m_desc.OutputWindow,
|
|
|
|
newRect.left, newRect.top,
|
|
|
|
newRect.right - newRect.left,
|
|
|
|
newRect.bottom - newRect.top, TRUE);
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2017-12-05 14:43:03 +01:00
|
|
|
try {
|
|
|
|
m_presenter->recreateSwapchain(
|
|
|
|
m_desc.BufferDesc.Width,
|
|
|
|
m_desc.BufferDesc.Height,
|
|
|
|
m_desc.BufferDesc.Format);
|
|
|
|
return S_OK;
|
|
|
|
} catch (const DxvkError& e) {
|
|
|
|
Logger::err(e.message());
|
2017-11-26 15:29:57 +01:00
|
|
|
return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
|
|
|
|
}
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::SetFullscreenState(
|
|
|
|
BOOL Fullscreen,
|
|
|
|
IDXGIOutput* pTarget) {
|
2017-11-26 15:29:57 +01:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
|
2017-12-05 14:43:03 +01:00
|
|
|
Logger::err("DxgiSwapChain::SetFullscreenState: Not implemented yet");
|
|
|
|
return E_NOTIMPL;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2017-11-29 21:46:09 +01:00
|
|
|
void DxgiSwapChain::createPresenter() {
|
|
|
|
m_presenter = new DxgiPresenter(
|
2017-12-04 11:33:04 +01:00
|
|
|
m_device->GetDXVKDevice(),
|
2017-11-29 21:46:09 +01:00
|
|
|
m_desc.OutputWindow,
|
|
|
|
m_desc.BufferDesc.Width,
|
2017-12-04 22:21:02 +01:00
|
|
|
m_desc.BufferDesc.Height,
|
|
|
|
m_desc.BufferDesc.Format);
|
2017-11-29 21:46:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
void DxgiSwapChain::createBackBuffer() {
|
2017-12-04 11:33:04 +01:00
|
|
|
// Pick the back buffer format based on the requested swap chain format
|
|
|
|
DxgiFormatPair bufferFormat = m_adapter->LookupFormat(m_desc.BufferDesc.Format);
|
|
|
|
Logger::info(str::format("DxgiSwapChain: Creating back buffer with ", bufferFormat.actual));
|
2017-11-29 15:16:07 +01:00
|
|
|
|
2017-12-04 11:33:04 +01:00
|
|
|
// TODO support proper multi-sampling
|
|
|
|
const Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();
|
2017-11-29 15:16:07 +01:00
|
|
|
|
2017-11-29 08:29:12 +01:00
|
|
|
// Create an image that can be rendered to
|
|
|
|
// and that can be used as a sampled texture.
|
2017-11-29 15:16:07 +01:00
|
|
|
Com<IDXGIImageResourcePrivate> resource;
|
|
|
|
|
2017-11-29 08:29:12 +01:00
|
|
|
DxvkImageCreateInfo imageInfo;
|
|
|
|
imageInfo.type = VK_IMAGE_TYPE_2D;
|
2017-12-04 11:33:04 +01:00
|
|
|
imageInfo.format = bufferFormat.actual;
|
2017-12-05 14:43:03 +01:00
|
|
|
imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
2017-11-29 08:29:12 +01:00
|
|
|
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
imageInfo.extent.width = m_desc.BufferDesc.Width;
|
|
|
|
imageInfo.extent.height = m_desc.BufferDesc.Height;
|
|
|
|
imageInfo.extent.depth = 1;
|
|
|
|
imageInfo.numLayers = 1;
|
|
|
|
imageInfo.mipLevels = 1;
|
|
|
|
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
|
|
|
| VK_IMAGE_USAGE_SAMPLED_BIT
|
|
|
|
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
|
|
|
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
|
|
imageInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
|
|
|
|
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
|
|
|
|
| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
|
|
|
|
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
|
|
|
|
| VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
|
|
imageInfo.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
2017-12-02 11:46:25 +01:00
|
|
|
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
2017-11-29 08:29:12 +01:00
|
|
|
| VK_ACCESS_TRANSFER_WRITE_BIT
|
|
|
|
| VK_ACCESS_TRANSFER_READ_BIT
|
|
|
|
| VK_ACCESS_SHADER_READ_BIT;
|
|
|
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
2017-12-03 00:40:58 +01:00
|
|
|
imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
2017-11-29 08:29:12 +01:00
|
|
|
|
2017-12-02 16:47:06 +01:00
|
|
|
if (dxvkDevice->features().geometryShader)
|
|
|
|
imageInfo.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
|
|
|
|
|
|
|
|
if (dxvkDevice->features().tessellationShader) {
|
|
|
|
imageInfo.stages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT
|
|
|
|
| VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
|
|
|
|
}
|
|
|
|
|
2017-12-04 11:33:04 +01:00
|
|
|
if (FAILED(DXGICreateImageResourcePrivate(m_device.ptr(), &imageInfo,
|
2017-11-29 15:16:07 +01:00
|
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, DXGI_USAGE_BACK_BUFFER | m_desc.BufferUsage,
|
|
|
|
&resource)))
|
|
|
|
throw DxvkError("DxgiSwapChain::createBackBuffer: Failed to create back buffer");
|
|
|
|
|
|
|
|
m_backBuffer = resource->GetDXVKImage();
|
2017-11-29 08:29:12 +01:00
|
|
|
|
|
|
|
// Create an image view that allows the
|
|
|
|
// image to be bound as a shader resource.
|
|
|
|
DxvkImageViewCreateInfo viewInfo;
|
|
|
|
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
viewInfo.format = imageInfo.format;
|
|
|
|
viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
viewInfo.minLevel = 0;
|
|
|
|
viewInfo.numLevels = 1;
|
|
|
|
viewInfo.minLayer = 0;
|
|
|
|
viewInfo.numLayers = 1;
|
|
|
|
|
|
|
|
m_backBufferView = dxvkDevice->createImageView(m_backBuffer, viewInfo);
|
|
|
|
|
|
|
|
// Wrap the back buffer image into an interface
|
|
|
|
// that the device can use to access the image.
|
2017-12-04 11:33:04 +01:00
|
|
|
if (FAILED(m_presentDevice->WrapSwapChainBackBuffer(resource.ptr(), &m_desc, &m_backBufferIface)))
|
2017-11-29 15:16:07 +01:00
|
|
|
throw DxvkError("DxgiSwapChain::createBackBuffer: Failed to create back buffer interface");
|
2017-12-02 11:46:25 +01:00
|
|
|
|
|
|
|
// Initialize the image properly so that
|
|
|
|
// it can be used in a DXVK context
|
|
|
|
m_presenter->initBackBuffer(m_backBuffer);
|
2017-11-29 07:55:44 +01:00
|
|
|
}
|
|
|
|
|
2017-12-04 22:21:02 +01:00
|
|
|
|
|
|
|
VkExtent2D DxgiSwapChain::getWindowSize() const {
|
2017-12-05 14:43:03 +01:00
|
|
|
RECT rect;
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2017-12-05 14:43:03 +01:00
|
|
|
if (::GetClientRect(m_desc.OutputWindow, &rect)) {
|
|
|
|
VkExtent2D result;
|
|
|
|
result.width = rect.right - rect.left;
|
|
|
|
result.height = rect.bottom - rect.top;
|
|
|
|
return result;
|
|
|
|
}
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2017-12-05 14:43:03 +01:00
|
|
|
throw DxvkError("DxgiSwapChain::getWindowSize: Failed to get window rect");
|
2017-12-04 22:21:02 +01:00
|
|
|
}
|
|
|
|
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|