2018-01-13 18:56:15 +01:00
|
|
|
#include "dxgi_device.h"
|
2017-10-11 16:22:13 +02:00
|
|
|
#include "dxgi_factory.h"
|
2018-04-10 22:07:25 +02:00
|
|
|
#include "dxgi_output.h"
|
2017-10-11 16:22:13 +02:00
|
|
|
#include "dxgi_swapchain.h"
|
|
|
|
|
|
|
|
namespace dxvk {
|
|
|
|
|
|
|
|
DxgiSwapChain::DxgiSwapChain(
|
2018-05-23 01:06:34 +02:00
|
|
|
DxgiFactory* pFactory,
|
|
|
|
IUnknown* pDevice,
|
|
|
|
HWND hWnd,
|
|
|
|
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
|
|
|
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc)
|
|
|
|
: m_factory (pFactory),
|
|
|
|
m_window (hWnd),
|
2018-04-12 13:38:22 +02:00
|
|
|
m_desc (*pDesc),
|
2018-05-23 01:06:34 +02:00
|
|
|
m_descFs (*pFullscreenDesc),
|
2018-04-12 13:38:22 +02:00
|
|
|
m_monitor (nullptr) {
|
2017-11-26 15:29:57 +01:00
|
|
|
// Retrieve a device pointer that allows us to
|
|
|
|
// communicate with the underlying D3D device
|
2018-03-28 19:06:00 +02:00
|
|
|
if (FAILED(pDevice->QueryInterface(__uuidof(IDXGIVkPresenter),
|
2017-12-04 11:33:04 +01:00
|
|
|
reinterpret_cast<void**>(&m_presentDevice))))
|
2018-04-14 12:02:55 +02:00
|
|
|
throw DxvkError("DXGI: DxgiSwapChain: Invalid device");
|
2017-11-26 14:02:08 +01:00
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
// Retrieve the adapter, which is going
|
|
|
|
// to be used to enumerate displays.
|
2018-01-13 18:56:15 +01:00
|
|
|
Com<IDXGIDevice> device;
|
2017-12-04 11:33:04 +01:00
|
|
|
Com<IDXGIAdapter> adapter;
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2018-01-13 18:56:15 +01:00
|
|
|
if (FAILED(pDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&device))))
|
2018-04-14 12:02:55 +02:00
|
|
|
throw DxvkError("DXGI: DxgiSwapChain: Invalid device");
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2018-01-13 18:56:15 +01:00
|
|
|
if (FAILED(device->GetAdapter(&adapter)))
|
2018-04-14 12:02:55 +02:00
|
|
|
throw DxvkError("DXGI: DxgiSwapChain: Failed to retrieve adapter");
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2018-01-13 18:56:15 +01:00
|
|
|
m_device = static_cast<DxgiDevice*>(device.ptr());
|
|
|
|
m_adapter = static_cast<DxgiAdapter*>(adapter.ptr());
|
|
|
|
|
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.
|
2017-12-31 00:23:34 +01:00
|
|
|
const VkExtent2D windowSize = GetWindowSize();
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (m_desc.Width == 0) m_desc.Width = windowSize.width;
|
|
|
|
if (m_desc.Height == 0) m_desc.Height = windowSize.height;
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
// Set initial window mode and fullscreen state
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!m_descFs.Windowed && FAILED(EnterFullscreenMode(nullptr)))
|
2018-04-14 12:02:55 +02:00
|
|
|
throw DxvkError("DXGI: DxgiSwapChain: Failed to set initial fullscreen state");
|
2017-11-26 18:38:50 +01:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
if (FAILED(CreatePresenter()) || FAILED(CreateBackBuffer()))
|
2018-04-14 12:02:55 +02:00
|
|
|
throw DxvkError("DXGI: DxgiSwapChain: Failed to create presenter or back buffer");
|
2018-04-10 20:44:55 +02:00
|
|
|
|
2018-04-10 22:07:25 +02:00
|
|
|
if (FAILED(SetDefaultGammaControl()))
|
2018-04-14 12:02:55 +02:00
|
|
|
throw DxvkError("DXGI: DxgiSwapChain: Failed to set up gamma ramp");
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxgiSwapChain::~DxgiSwapChain() {
|
2018-04-30 20:15:35 +02:00
|
|
|
Com<IDXGIOutput> output;
|
|
|
|
|
|
|
|
if (SUCCEEDED(m_adapter->GetOutputFromMonitor(m_monitor, &output)))
|
|
|
|
RestoreDisplayMode(output.ptr());
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::QueryInterface(REFIID riid, void** ppvObject) {
|
2018-04-02 12:52:02 +02:00
|
|
|
*ppvObject = nullptr;
|
|
|
|
|
|
|
|
if (riid == __uuidof(IUnknown)
|
|
|
|
|| riid == __uuidof(IDXGIObject)
|
|
|
|
|| riid == __uuidof(IDXGIDeviceSubObject)
|
2018-05-22 23:50:28 +02:00
|
|
|
|| riid == __uuidof(IDXGISwapChain)
|
|
|
|
|| riid == __uuidof(IDXGISwapChain1)) {
|
2018-04-02 12:52:02 +02:00
|
|
|
*ppvObject = ref(this);
|
|
|
|
return S_OK;
|
|
|
|
}
|
2017-10-11 16:22:13 +02:00
|
|
|
|
|
|
|
Logger::warn("DxgiSwapChain::QueryInterface: Unknown interface query");
|
2018-03-12 14:05:43 +03:00
|
|
|
Logger::warn(str::format(riid));
|
2017-10-11 16:22:13 +02:00
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE 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
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE 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
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) {
|
2018-04-02 12:04:20 +02:00
|
|
|
InitReturnPtr(ppSurface);
|
|
|
|
|
2018-02-14 13:11:59 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
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-12-19 16:01:50 +01:00
|
|
|
return m_backBuffer->QueryInterface(riid, ppSurface);
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetContainingOutput(IDXGIOutput** ppOutput) {
|
2018-04-02 12:04:20 +02:00
|
|
|
InitReturnPtr(ppOutput);
|
|
|
|
|
2018-04-18 17:30:46 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2018-01-13 18:56:15 +01:00
|
|
|
RECT windowRect = { 0, 0, 0, 0 };
|
2018-05-23 01:06:34 +02:00
|
|
|
::GetWindowRect(m_window, &windowRect);
|
2018-01-13 18:56:15 +01:00
|
|
|
|
|
|
|
HMONITOR monitor = ::MonitorFromPoint(
|
|
|
|
{ (windowRect.left + windowRect.right) / 2,
|
|
|
|
(windowRect.top + windowRect.bottom) / 2 },
|
|
|
|
MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
|
|
|
|
return m_adapter->GetOutputFromMonitor(monitor, ppOutput);
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) {
|
2018-04-18 17:30:46 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pDesc == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
pDesc->BufferDesc.Width = m_desc.Width;
|
|
|
|
pDesc->BufferDesc.Height = m_desc.Height;
|
|
|
|
pDesc->BufferDesc.RefreshRate = m_descFs.RefreshRate;
|
|
|
|
pDesc->BufferDesc.Format = m_desc.Format;
|
|
|
|
pDesc->BufferDesc.ScanlineOrdering = m_descFs.ScanlineOrdering;
|
|
|
|
pDesc->BufferDesc.Scaling = m_descFs.Scaling;
|
|
|
|
pDesc->SampleDesc = m_desc.SampleDesc;
|
|
|
|
pDesc->BufferUsage = m_desc.BufferUsage;
|
|
|
|
pDesc->BufferCount = m_desc.BufferCount;
|
|
|
|
pDesc->OutputWindow = m_window;
|
|
|
|
pDesc->Windowed = m_descFs.Windowed;
|
|
|
|
pDesc->SwapEffect = m_desc.SwapEffect;
|
|
|
|
pDesc->Flags = m_desc.Flags;
|
2017-11-26 15:29:57 +01:00
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-22 23:50:28 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) {
|
2018-05-23 01:06:34 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
|
|
|
if (pDesc == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
*pDesc = m_desc;
|
|
|
|
return S_OK;
|
2018-05-22 23:50:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBackgroundColor(
|
|
|
|
DXGI_RGBA* pColor) {
|
|
|
|
Logger::err("DxgiSwapChain::GetBackgroundColor: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRotation(
|
|
|
|
DXGI_MODE_ROTATION* pRotation) {
|
|
|
|
Logger::err("DxgiSwapChain::GetRotation: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRestrictToOutput(
|
|
|
|
IDXGIOutput** ppRestrictToOutput) {
|
|
|
|
InitReturnPtr(ppRestrictToOutput);
|
|
|
|
|
|
|
|
Logger::err("DxgiSwapChain::GetRestrictToOutput: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
|
2018-04-18 17:30:46 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pStats == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
*pStats = m_stats;
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenState(
|
2017-10-11 16:22:13 +02:00
|
|
|
BOOL* pFullscreen,
|
|
|
|
IDXGIOutput** ppTarget) {
|
2018-02-14 13:11:59 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
if (pFullscreen != nullptr)
|
2018-05-23 01:06:34 +02:00
|
|
|
*pFullscreen = !m_descFs.Windowed;
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-03-27 02:58:33 +03:00
|
|
|
if (ppTarget != nullptr) {
|
|
|
|
*ppTarget = nullptr;
|
2018-03-28 11:56:58 +02:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!m_descFs.Windowed)
|
2018-05-04 17:37:29 +02:00
|
|
|
hr = m_adapter->GetOutputFromMonitor(m_monitor, ppTarget);
|
2018-03-27 02:58:33 +03:00
|
|
|
}
|
2017-11-26 15:29:57 +01:00
|
|
|
|
|
|
|
return hr;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-22 23:50:28 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc(
|
|
|
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) {
|
2018-05-23 01:06:34 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
|
|
|
if (pDesc == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
*pDesc = m_descFs;
|
|
|
|
return S_OK;
|
2018-05-22 23:50:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetHwnd(
|
|
|
|
HWND* pHwnd) {
|
2018-05-23 01:06:34 +02:00
|
|
|
if (pHwnd == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
*pHwnd = m_window;
|
|
|
|
return S_OK;
|
2018-05-22 23:50:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetCoreWindow(
|
|
|
|
REFIID refiid,
|
|
|
|
void** ppUnk) {
|
2018-05-23 01:06:34 +02:00
|
|
|
InitReturnPtr(ppUnk);
|
|
|
|
|
2018-05-22 23:50:28 +02:00
|
|
|
Logger::err("DxgiSwapChain::GetCoreWindow: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) {
|
2018-04-18 17:30:46 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pLastPresentCount == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
*pLastPresentCount = m_stats.PresentCount;
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-22 23:50:28 +02:00
|
|
|
BOOL STDMETHODCALLTYPE DxgiSwapChain::IsTemporaryMonoSupported() {
|
|
|
|
// This seems to be related to stereo 3D display
|
|
|
|
// modes, which we don't support at the moment
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {
|
2018-02-14 13:11:59 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2018-02-18 23:49:00 +01:00
|
|
|
if (Flags & DXGI_PRESENT_TEST)
|
|
|
|
return S_OK;
|
|
|
|
|
2018-05-28 21:06:35 +02:00
|
|
|
// Higher values are not allowed according to the Microsoft documentation:
|
|
|
|
//
|
|
|
|
// "1 through 4 - Synchronize presentation after the nth vertical blank."
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb174576(v=vs.85).aspx
|
|
|
|
SyncInterval = std::min<UINT>(SyncInterval, 4);
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
try {
|
2018-04-12 13:38:22 +02:00
|
|
|
// If in fullscreen mode, apply any updated gamma curve
|
|
|
|
// if it has been changed since the last present call.
|
|
|
|
DXGI_VK_OUTPUT_DATA outputData;
|
|
|
|
|
|
|
|
if (SUCCEEDED(m_adapter->GetOutputData(m_monitor, &outputData)) && outputData.GammaDirty) {
|
|
|
|
SetGammaControl(&outputData.GammaCurve);
|
|
|
|
|
|
|
|
outputData.GammaDirty = FALSE;
|
|
|
|
m_adapter->SetOutputData(m_monitor, &outputData);
|
|
|
|
}
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
// Submit pending rendering commands
|
|
|
|
// before recording the present code.
|
2017-12-04 11:33:04 +01:00
|
|
|
m_presentDevice->FlushRenderingCommands();
|
2017-12-31 00:23:34 +01:00
|
|
|
|
|
|
|
// Update swap chain properties. This will not only set
|
|
|
|
// up vertical synchronization properly, but also apply
|
|
|
|
// changes that were made to the window size even if the
|
|
|
|
// Vulkan swap chain itself remains valid.
|
2018-05-23 13:03:12 +02:00
|
|
|
VkPresentModeKHR presentMode = SyncInterval == 0
|
|
|
|
? VK_PRESENT_MODE_IMMEDIATE_KHR
|
|
|
|
: VK_PRESENT_MODE_FIFO_KHR;
|
2018-05-28 21:06:35 +02:00
|
|
|
|
2018-05-23 13:03:12 +02:00
|
|
|
m_presenter->RecreateSwapchain(m_desc.Format, presentMode, GetWindowSize());
|
2018-05-28 21:06:35 +02:00
|
|
|
m_presenter->PresentImage(SyncInterval);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-22 23:50:28 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1(
|
|
|
|
UINT SyncInterval,
|
|
|
|
UINT PresentFlags,
|
|
|
|
const DXGI_PRESENT_PARAMETERS* pPresentParameters) {
|
|
|
|
if (pPresentParameters != nullptr)
|
|
|
|
Logger::warn("DXGI: Present parameters not supported");
|
|
|
|
|
|
|
|
return Present(SyncInterval, PresentFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers(
|
2017-10-11 16:22:13 +02:00
|
|
|
UINT BufferCount,
|
|
|
|
UINT Width,
|
|
|
|
UINT Height,
|
|
|
|
DXGI_FORMAT NewFormat,
|
|
|
|
UINT SwapChainFlags) {
|
2018-02-14 13:11:59 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
const VkExtent2D windowSize = GetWindowSize();
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
m_desc.Width = Width != 0 ? Width : windowSize.width;
|
|
|
|
m_desc.Height = Height != 0 ? Height : windowSize.height;
|
2017-12-04 22:21:02 +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)
|
2018-05-23 01:06:34 +02:00
|
|
|
m_desc.Format = NewFormat;
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
return CreateBackBuffer();
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) {
|
2018-04-18 17:30:46 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2017-11-26 15:29:57 +01:00
|
|
|
if (pNewTargetParameters == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-04-29 23:03:27 +02:00
|
|
|
// Update the swap chain description
|
|
|
|
if (pNewTargetParameters->RefreshRate.Numerator != 0)
|
2018-05-23 01:06:34 +02:00
|
|
|
m_descFs.RefreshRate = pNewTargetParameters->RefreshRate;
|
2018-04-29 23:03:27 +02:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
m_descFs.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering;
|
|
|
|
m_descFs.Scaling = pNewTargetParameters->Scaling;
|
2018-04-29 23:03:27 +02:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (m_descFs.Windowed) {
|
2018-04-29 23:03:27 +02:00
|
|
|
// Adjust window position and size
|
|
|
|
RECT newRect = { 0, 0, 0, 0 };
|
|
|
|
RECT oldRect = { 0, 0, 0, 0 };
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
::GetWindowRect(m_window, &oldRect);
|
2018-04-29 23:03:27 +02:00
|
|
|
::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
|
|
|
|
::AdjustWindowRectEx(&newRect,
|
2018-05-23 01:06:34 +02:00
|
|
|
::GetWindowLongW(m_window, GWL_STYLE), FALSE,
|
|
|
|
::GetWindowLongW(m_window, GWL_EXSTYLE));
|
2018-04-29 23:03:27 +02:00
|
|
|
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
|
|
|
|
::OffsetRect(&newRect, oldRect.left, oldRect.top);
|
2018-05-23 01:06:34 +02:00
|
|
|
::MoveWindow(m_window, newRect.left, newRect.top,
|
2018-04-29 23:03:27 +02:00
|
|
|
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
|
|
|
|
} else {
|
|
|
|
Com<IDXGIOutput> output;
|
|
|
|
|
|
|
|
if (FAILED(m_adapter->GetOutputFromMonitor(m_monitor, &output))) {
|
|
|
|
Logger::err("DXGI: ResizeTarget: Failed to query containing output");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the swap chain allows it, change the display mode
|
|
|
|
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH)
|
|
|
|
ChangeDisplayMode(output.ptr(), pNewTargetParameters);
|
|
|
|
|
|
|
|
// Resize and reposition the window to
|
|
|
|
DXGI_OUTPUT_DESC desc;
|
|
|
|
output->GetDesc(&desc);
|
|
|
|
|
|
|
|
const RECT newRect = desc.DesktopCoordinates;
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
::MoveWindow(m_window, newRect.left, newRect.top,
|
2018-04-29 23:03:27 +02:00
|
|
|
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
|
|
|
|
}
|
2017-12-05 15:20:03 +01:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 12:50:52 +01:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetFullscreenState(
|
2017-10-11 16:22:13 +02:00
|
|
|
BOOL Fullscreen,
|
|
|
|
IDXGIOutput* pTarget) {
|
2018-02-14 13:11:59 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
2017-11-26 15:29:57 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!IsWindow(m_window))
|
2018-04-18 17:30:46 +02:00
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (m_descFs.Windowed && Fullscreen)
|
2018-01-13 18:56:15 +01:00
|
|
|
return this->EnterFullscreenMode(pTarget);
|
2018-05-23 01:06:34 +02:00
|
|
|
else if (!m_descFs.Windowed && !Fullscreen)
|
2018-01-13 18:56:15 +01:00
|
|
|
return this->LeaveFullscreenMode();
|
|
|
|
|
|
|
|
return S_OK;
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|
|
|
|
|
2017-11-29 07:55:44 +01:00
|
|
|
|
2018-05-22 23:50:28 +02:00
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetBackgroundColor(
|
|
|
|
const DXGI_RGBA* pColor) {
|
|
|
|
Logger::err("DxgiSwapChain::SetBackgroundColor: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetRotation(
|
|
|
|
DXGI_MODE_ROTATION Rotation) {
|
|
|
|
Logger::err("DxgiSwapChain::SetRotation: Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-13 13:47:15 +02:00
|
|
|
HRESULT DxgiSwapChain::SetGammaControl(const DXGI_GAMMA_CONTROL* pGammaControl) {
|
2018-04-10 22:07:25 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2018-04-13 13:47:15 +02:00
|
|
|
DXGI_VK_GAMMA_CURVE curve;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
|
|
|
|
const DXGI_RGB cp = pGammaControl->GammaCurve[i];
|
|
|
|
curve.ControlPoints[i].R = MapGammaControlPoint(cp.Red);
|
|
|
|
curve.ControlPoints[i].G = MapGammaControlPoint(cp.Green);
|
|
|
|
curve.ControlPoints[i].B = MapGammaControlPoint(cp.Blue);
|
|
|
|
curve.ControlPoints[i].A = 0;
|
2018-04-10 22:07:25 +02:00
|
|
|
}
|
|
|
|
|
2018-04-15 20:12:41 +02:00
|
|
|
m_presenter->SetGammaControl(&curve);
|
2018-04-10 22:07:25 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::SetDefaultGammaControl() {
|
2018-04-10 20:44:55 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
|
|
|
|
2018-04-13 13:47:15 +02:00
|
|
|
DXGI_VK_GAMMA_CURVE curve;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
|
|
|
|
const uint16_t value = MapGammaControlPoint(
|
|
|
|
float(i) / float(DXGI_VK_GAMMA_CP_COUNT - 1));
|
|
|
|
curve.ControlPoints[i] = { value, value, value, 0 };
|
2018-04-10 20:44:55 +02:00
|
|
|
}
|
|
|
|
|
2018-04-15 20:12:41 +02:00
|
|
|
m_presenter->SetGammaControl(&curve);
|
2018-04-10 20:44:55 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
HRESULT DxgiSwapChain::CreatePresenter() {
|
|
|
|
try {
|
2018-04-13 13:57:29 +02:00
|
|
|
m_presenter = new DxgiVkPresenter(
|
2017-12-31 00:23:34 +01:00
|
|
|
m_device->GetDXVKDevice(),
|
2018-05-23 01:06:34 +02:00
|
|
|
m_window);
|
2017-12-31 00:23:34 +01:00
|
|
|
return S_OK;
|
|
|
|
} catch (const DxvkError& e) {
|
|
|
|
Logger::err(e.message());
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2017-11-29 21:46:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
HRESULT DxgiSwapChain::CreateBackBuffer() {
|
2018-03-18 20:39:14 +01:00
|
|
|
// Figure out sample count based on swap chain description
|
2017-12-12 00:27:49 +01:00
|
|
|
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
2017-12-02 16:47:06 +01:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
if (FAILED(GetSampleCount(m_desc.SampleDesc.Count, &sampleCount))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: CreateBackBuffer: Invalid sample count");
|
2017-12-31 00:23:34 +01:00
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
2017-11-29 08:29:12 +01:00
|
|
|
|
2018-03-18 20:39:14 +01:00
|
|
|
// Destroy previous back buffer before creating a new one
|
|
|
|
m_backBuffer = nullptr;
|
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
if (FAILED(m_presentDevice->CreateSwapChainBackBuffer(&m_desc, &m_backBuffer))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: CreateBackBuffer: Failed to create back buffer");
|
2017-12-31 00:23:34 +01:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
2017-11-29 08:29:12 +01:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
try {
|
2018-04-13 13:57:29 +02:00
|
|
|
m_presenter->UpdateBackBuffer(m_backBuffer->GetDXVKImage());
|
2017-12-31 00:23:34 +01:00
|
|
|
return S_OK;
|
|
|
|
} catch (const DxvkError& e) {
|
|
|
|
Logger::err(e.message());
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2017-11-29 07:55:44 +01:00
|
|
|
}
|
|
|
|
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2017-12-31 00:23:34 +01:00
|
|
|
VkExtent2D DxgiSwapChain::GetWindowSize() const {
|
|
|
|
RECT windowRect;
|
2018-04-18 17:30:46 +02:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
if (!::GetClientRect(m_window, &windowRect))
|
2018-04-18 17:30:46 +02:00
|
|
|
windowRect = RECT();
|
2017-12-04 22:21:02 +01:00
|
|
|
|
2017-12-05 15:20:03 +01:00
|
|
|
VkExtent2D result;
|
2017-12-31 00:23:34 +01:00
|
|
|
result.width = windowRect.right;
|
|
|
|
result.height = windowRect.bottom;
|
2017-12-05 15:20:03 +01:00
|
|
|
return result;
|
2017-12-04 22:21:02 +01:00
|
|
|
}
|
|
|
|
|
2017-12-12 00:27:49 +01:00
|
|
|
|
2018-04-14 12:02:55 +02:00
|
|
|
HRESULT DxgiSwapChain::EnterFullscreenMode(IDXGIOutput* pTarget) {
|
2018-04-12 01:28:44 +02:00
|
|
|
Com<IDXGIOutput> output = static_cast<DxgiOutput*>(pTarget);
|
2018-01-13 18:56:15 +01:00
|
|
|
|
2018-04-12 01:28:44 +02:00
|
|
|
if (output == nullptr) {
|
2018-04-12 00:45:23 +02:00
|
|
|
if (FAILED(GetContainingOutput(&output))) {
|
2018-04-14 12:02:55 +02:00
|
|
|
Logger::err("DXGI: EnterFullscreenMode: Cannot query containing output");
|
2018-01-13 18:56:15 +01:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-29 23:03:27 +02:00
|
|
|
// Find a display mode that matches what we need
|
2018-05-23 01:06:34 +02:00
|
|
|
::GetWindowRect(m_window, &m_windowState.rect);
|
2018-04-29 23:03:27 +02:00
|
|
|
|
|
|
|
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) {
|
|
|
|
auto windowRect = m_windowState.rect;
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
DXGI_MODE_DESC displayMode;
|
|
|
|
displayMode.Width = windowRect.right - windowRect.left;
|
|
|
|
displayMode.Height = windowRect.bottom - windowRect.top;
|
|
|
|
displayMode.RefreshRate = m_descFs.RefreshRate;
|
|
|
|
displayMode.Format = m_desc.Format;
|
|
|
|
displayMode.ScanlineOrdering = m_descFs.ScanlineOrdering;
|
|
|
|
displayMode.Scaling = m_descFs.Scaling;
|
2018-04-29 23:03:27 +02:00
|
|
|
|
|
|
|
if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) {
|
|
|
|
Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode");
|
|
|
|
return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 13:11:59 +01:00
|
|
|
// Update swap chain description
|
2018-05-23 01:06:34 +02:00
|
|
|
m_descFs.Windowed = FALSE;
|
2018-02-14 13:11:59 +01:00
|
|
|
|
2018-01-13 18:56:15 +01:00
|
|
|
// Change the window flags to remove the decoration etc.
|
2018-05-23 01:06:34 +02:00
|
|
|
LONG style = ::GetWindowLongW(m_window, GWL_STYLE);
|
|
|
|
LONG exstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE);
|
2018-01-13 18:56:15 +01:00
|
|
|
|
|
|
|
m_windowState.style = style;
|
|
|
|
m_windowState.exstyle = exstyle;
|
|
|
|
|
2018-05-04 21:55:38 +02:00
|
|
|
style &= ~WS_OVERLAPPEDWINDOW;
|
|
|
|
exstyle &= ~WS_EX_OVERLAPPEDWINDOW;
|
2018-01-13 18:56:15 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
::SetWindowLongW(m_window, GWL_STYLE, style);
|
|
|
|
::SetWindowLongW(m_window, GWL_EXSTYLE, exstyle);
|
2018-01-13 18:56:15 +01:00
|
|
|
|
|
|
|
// Move the window so that it covers the entire output
|
|
|
|
DXGI_OUTPUT_DESC desc;
|
2018-04-12 01:28:44 +02:00
|
|
|
output->GetDesc(&desc);
|
2018-01-13 18:56:15 +01:00
|
|
|
|
|
|
|
const RECT rect = desc.DesktopCoordinates;
|
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
::SetWindowPos(m_window, HWND_TOPMOST,
|
2018-01-13 18:56:15 +01:00
|
|
|
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
|
|
|
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
|
2018-04-12 13:38:22 +02:00
|
|
|
|
|
|
|
m_monitor = desc.Monitor;
|
2018-01-13 18:56:15 +01:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::LeaveFullscreenMode() {
|
2018-04-29 23:03:27 +02:00
|
|
|
Com<IDXGIOutput> output;
|
|
|
|
|
|
|
|
if (FAILED(m_adapter->GetOutputFromMonitor(m_monitor, &output))
|
|
|
|
|| FAILED(RestoreDisplayMode(output.ptr())))
|
|
|
|
Logger::warn("DXGI: LeaveFullscreenMode: Failed to restore display mode");
|
|
|
|
|
|
|
|
// Restore internal state
|
2018-05-23 01:06:34 +02:00
|
|
|
m_descFs.Windowed = TRUE;
|
2018-04-12 13:38:22 +02:00
|
|
|
m_monitor = nullptr;
|
2018-02-14 13:11:59 +01:00
|
|
|
|
2018-05-04 21:55:38 +02:00
|
|
|
// Only restore the window style if the application hasn't
|
|
|
|
// changed them. This is in line with what native DXGI does.
|
2018-05-23 01:06:34 +02:00
|
|
|
LONG curStyle = ::GetWindowLongW(m_window, GWL_STYLE) & ~WS_VISIBLE;
|
|
|
|
LONG curExstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
|
2018-01-13 18:56:15 +01:00
|
|
|
|
2018-05-04 21:55:38 +02:00
|
|
|
if (curStyle == (m_windowState.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
|
|
|
|
&& curExstyle == (m_windowState.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
|
2018-05-23 01:06:34 +02:00
|
|
|
::SetWindowLongW(m_window, GWL_STYLE, m_windowState.style);
|
|
|
|
::SetWindowLongW(m_window, GWL_EXSTYLE, m_windowState.exstyle);
|
2018-05-04 21:55:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Restore window position and apply the style
|
|
|
|
const RECT rect = m_windowState.rect;
|
2018-01-13 18:56:15 +01:00
|
|
|
|
2018-05-23 01:06:34 +02:00
|
|
|
::SetWindowPos(m_window, 0,
|
2018-01-13 18:56:15 +01:00
|
|
|
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
|
|
|
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
|
2018-04-12 13:38:22 +02:00
|
|
|
return SetDefaultGammaControl();
|
2018-01-13 18:56:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-29 23:03:27 +02:00
|
|
|
HRESULT DxgiSwapChain::ChangeDisplayMode(
|
|
|
|
IDXGIOutput* pOutput,
|
|
|
|
const DXGI_MODE_DESC* pDisplayMode) {
|
|
|
|
auto output = static_cast<DxgiOutput*>(pOutput);
|
|
|
|
|
2018-05-04 21:55:38 +02:00
|
|
|
if (output == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
// Find a mode that the output supports
|
2018-04-29 23:03:27 +02:00
|
|
|
DXGI_MODE_DESC selectedMode;
|
|
|
|
|
|
|
|
HRESULT hr = output->FindClosestMatchingMode(
|
|
|
|
pDisplayMode, &selectedMode, nullptr);
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
return output->SetDisplayMode(&selectedMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT DxgiSwapChain::RestoreDisplayMode(IDXGIOutput* pOutput) {
|
|
|
|
auto output = static_cast<DxgiOutput*>(pOutput);
|
2018-05-04 21:55:38 +02:00
|
|
|
|
|
|
|
if (output == nullptr)
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
|
|
|
|
// Restore registry settings
|
2018-04-29 23:03:27 +02:00
|
|
|
DXGI_MODE_DESC mode;
|
|
|
|
|
2018-05-04 21:55:38 +02:00
|
|
|
HRESULT hr = output->GetDisplayMode(
|
|
|
|
&mode, ENUM_REGISTRY_SETTINGS);
|
2018-04-29 23:03:27 +02:00
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
return output->SetDisplayMode(&mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-12 00:27:49 +01:00
|
|
|
HRESULT DxgiSwapChain::GetSampleCount(UINT Count, VkSampleCountFlagBits* pCount) const {
|
|
|
|
switch (Count) {
|
|
|
|
case 1: *pCount = VK_SAMPLE_COUNT_1_BIT; return S_OK;
|
|
|
|
case 2: *pCount = VK_SAMPLE_COUNT_2_BIT; return S_OK;
|
|
|
|
case 4: *pCount = VK_SAMPLE_COUNT_4_BIT; return S_OK;
|
|
|
|
case 8: *pCount = VK_SAMPLE_COUNT_8_BIT; return S_OK;
|
|
|
|
case 16: *pCount = VK_SAMPLE_COUNT_16_BIT; return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
2017-10-11 16:22:13 +02:00
|
|
|
}
|