mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-12-01 16:24:12 +01:00
Version 0.53
-----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEJz0EC1ETuIbRoJDUyMxhNCejHJkFAlsLDL4ACgkQyMxhNCej HJmIPwgArq3gx2qLKKLokrCyVbwf5UfYtMbnyEQCs7nSBbb0JiaeQXJClAdIwFP6 VncCpCDOjvssHAiXMF1hfbHDMvVpMa3nwz+O2bOKFxg9OCt69T0wqbOvNVuJzEq4 7zXWNBXegPJaY5KQXiJORetU/xNcb8/ikWEAT57vkRu2RvvT2ct/oaynWZtgh+X5 /OMHW1nMP9Bvwm5ZCWw2fCdT9evqIrXL3IreoJKX+dW/10oIaUnh+Q5Fcm7L0s7i dYkmm1KM6WiHpO/duK0SpbOl9ASzorwtcRjgM2syzM0QljcUpdnRCDXsuVWYOpRL 7GbtyzdVC/SJknJoWMdTcpIX6fjYPA== =nkRK -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEJz0EC1ETuIbRoJDUyMxhNCejHJkFAlsOvCkACgkQyMxhNCej HJlusQgAkfcCUgFLMLGoUeOJaHkpDnv5/s13AMG3a+m9SLUavQ87izysrp6cEaF6 1O1Rxf/NHqhOh8jGwHILVmDWeYzDGkNKUW0/g0A0hcLMDyh5f5zMVqYoX9ITsjmG w5Woc4I7JPqsPdqJ0FOk/dQKIrnVXr/SZXrna55ZvXPI9q1wz0QCbE4E/q47tJUA 3F5yw+eNaRWEHe7T9wSXdtuVo7R1NFqewt5kTvgiYg6HCWSCNAvgTVHnEg2tpaNC rzipvxXKSNbDB30JpC8+R6aP4b7z6P9p1KjyoSKT7Jb5kvOw3p6eY5WQq69KUhhq EjTMwUrFaP1K6IJwgWfxLxIufp5pqw== =JJRk -----END PGP SIGNATURE----- Merge tag 'v0.53' into openvr-v2 Version 0.53
This commit is contained in:
commit
7fe4a70342
@ -6,6 +6,8 @@ For the current status of the project, please refer to the [project wiki](https:
|
||||
|
||||
For binary releases, see the [releases](https://github.com/doitsujin/dxvk/releases) page.
|
||||
|
||||
For Direct3D 10 support, check out [DXUP](https://github.com/Joshua-Ashton/dxup), which can be used together with DXVK.
|
||||
|
||||
## Build instructions
|
||||
|
||||
### Requirements:
|
||||
|
@ -856,9 +856,7 @@ namespace dxvk {
|
||||
if (view->GetResourceType() != D3D11_RESOURCE_DIMENSION_BUFFER) {
|
||||
EmitCs([cDstImageView = view->GetImageView()]
|
||||
(DxvkContext* ctx) {
|
||||
ctx->generateMipmaps(
|
||||
cDstImageView->image(),
|
||||
cDstImageView->subresources());
|
||||
ctx->generateMipmaps(cDstImageView);
|
||||
});
|
||||
} else {
|
||||
Logger::err("D3D11: GenerateMips called on a buffer");
|
||||
@ -1237,8 +1235,18 @@ namespace dxvk {
|
||||
auto inputLayout = static_cast<D3D11InputLayout*>(pInputLayout);
|
||||
|
||||
if (m_state.ia.inputLayout != inputLayout) {
|
||||
bool equal = false;
|
||||
|
||||
// Some games (e.g. Grim Dawn) create lots and lots of
|
||||
// identical input layouts, so we'll only apply the state
|
||||
// if the input layouts has actually changed between calls.
|
||||
if (m_state.ia.inputLayout != nullptr && inputLayout != nullptr)
|
||||
equal = m_state.ia.inputLayout->Compare(inputLayout);
|
||||
|
||||
m_state.ia.inputLayout = inputLayout;
|
||||
ApplyInputLayout();
|
||||
|
||||
if (!equal)
|
||||
ApplyInputLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2290,8 +2298,11 @@ namespace dxvk {
|
||||
const D3D11_RECT* pRects) {
|
||||
m_state.rs.numScissors = NumRects;
|
||||
|
||||
for (uint32_t i = 0; i < NumRects; i++)
|
||||
m_state.rs.scissors.at(i) = pRects[i];
|
||||
for (uint32_t i = 0; i < NumRects; i++) {
|
||||
if (pRects[i].bottom >= pRects[i].top
|
||||
&& pRects[i].right >= pRects[i].left)
|
||||
m_state.rs.scissors.at(i) = pRects[i];
|
||||
}
|
||||
|
||||
if (m_state.rs.state != nullptr) {
|
||||
D3D11_RASTERIZER_DESC rsDesc;
|
||||
@ -2554,17 +2565,22 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < m_state.rs.numViewports; i++) {
|
||||
// TODO D3D11 docs aren't clear about what should happen
|
||||
// when there are undefined scissor rects for a viewport.
|
||||
// Figure out what it does on Windows.
|
||||
if (enableScissorTest && (i < m_state.rs.numScissors)) {
|
||||
const D3D11_RECT& sr = m_state.rs.scissors.at(i);
|
||||
D3D11_RECT sr = m_state.rs.scissors.at(i);
|
||||
|
||||
scissors.at(i) = VkRect2D {
|
||||
VkOffset2D { sr.left, sr.top },
|
||||
VkExtent2D {
|
||||
static_cast<uint32_t>(sr.right - sr.left),
|
||||
static_cast<uint32_t>(sr.bottom - sr.top) } };
|
||||
VkOffset2D srPosA;
|
||||
srPosA.x = std::max<int32_t>(0, sr.left);
|
||||
srPosA.y = std::max<int32_t>(0, sr.top);
|
||||
|
||||
VkOffset2D srPosB;
|
||||
srPosB.x = std::max<int32_t>(srPosA.x, sr.right);
|
||||
srPosB.y = std::max<int32_t>(srPosA.y, sr.bottom);
|
||||
|
||||
VkExtent2D srSize;
|
||||
srSize.width = uint32_t(srPosB.x - srPosA.x);
|
||||
srSize.height = uint32_t(srPosB.y - srPosA.y);
|
||||
|
||||
scissors.at(i) = VkRect2D { srPosA, srSize };
|
||||
} else {
|
||||
scissors.at(i) = VkRect2D {
|
||||
VkOffset2D { 0, 0 },
|
||||
|
@ -92,7 +92,7 @@ namespace dxvk {
|
||||
m_mappedResources.push_back(entry);
|
||||
|
||||
// Fill mapped resource structure
|
||||
pMappedResource->pData = entry.DataSlice.ptr();
|
||||
pMappedResource->pData = entry.MapPointer;
|
||||
pMappedResource->RowPitch = entry.RowPitch;
|
||||
pMappedResource->DepthPitch = entry.DepthPitch;
|
||||
return S_OK;
|
||||
@ -107,7 +107,7 @@ namespace dxvk {
|
||||
// Return same memory region as earlier
|
||||
entry->MapType = D3D11_MAP_WRITE_NO_OVERWRITE;
|
||||
|
||||
pMappedResource->pData = entry->DataSlice.ptr();
|
||||
pMappedResource->pData = entry->MapPointer;
|
||||
pMappedResource->RowPitch = entry->RowPitch;
|
||||
pMappedResource->DepthPitch = entry->DepthPitch;
|
||||
return S_OK;
|
||||
@ -145,9 +145,12 @@ namespace dxvk {
|
||||
D3D11_MAP MapType,
|
||||
UINT MapFlags,
|
||||
D3D11DeferredContextMapEntry* pMapEntry) {
|
||||
const D3D11Buffer* pBuffer = static_cast<D3D11Buffer*>(pResource);
|
||||
D3D11Buffer* pBuffer = static_cast<D3D11Buffer*>(pResource);
|
||||
const Rc<DxvkBuffer> buffer = pBuffer->GetBuffer();
|
||||
|
||||
D3D11_BUFFER_DESC bufferDesc;
|
||||
pBuffer->GetDesc(&bufferDesc);
|
||||
|
||||
if (!(buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
|
||||
Logger::err("D3D11: Cannot map a device-local buffer");
|
||||
return E_INVALIDARG;
|
||||
@ -158,7 +161,20 @@ namespace dxvk {
|
||||
pMapEntry->MapType = D3D11_MAP_WRITE_DISCARD;
|
||||
pMapEntry->RowPitch = pBuffer->GetSize();
|
||||
pMapEntry->DepthPitch = pBuffer->GetSize();
|
||||
pMapEntry->DataSlice = AllocUpdateBufferSlice(pBuffer->GetSize());
|
||||
|
||||
if (bufferDesc.Usage == D3D11_USAGE_DYNAMIC) {
|
||||
// For resources that cannot be written by the GPU,
|
||||
// we may write to the buffer resource directly and
|
||||
// just swap in the physical buffer slice as needed.
|
||||
pMapEntry->BufferSlice = buffer->allocPhysicalSlice();
|
||||
pMapEntry->MapPointer = pMapEntry->BufferSlice.mapPtr(0);
|
||||
} else {
|
||||
// For GPU-writable resources, we need a data slice
|
||||
// to perform the update operation at execution time.
|
||||
pMapEntry->DataSlice = AllocUpdateBufferSlice(pBuffer->GetSize());
|
||||
pMapEntry->MapPointer = pMapEntry->DataSlice.ptr();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -203,6 +219,7 @@ namespace dxvk {
|
||||
pMapEntry->RowPitch = xSize;
|
||||
pMapEntry->DepthPitch = ySize;
|
||||
pMapEntry->DataSlice = AllocUpdateBufferSlice(zSize);
|
||||
pMapEntry->MapPointer = pMapEntry->DataSlice.ptr();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -212,14 +229,26 @@ namespace dxvk {
|
||||
const D3D11DeferredContextMapEntry* pMapEntry) {
|
||||
D3D11Buffer* pBuffer = static_cast<D3D11Buffer*>(pResource);
|
||||
|
||||
EmitCs([
|
||||
cDstBuffer = pBuffer->GetBuffer(),
|
||||
cDataSlice = pMapEntry->DataSlice
|
||||
] (DxvkContext* ctx) {
|
||||
DxvkPhysicalBufferSlice slice = cDstBuffer->allocPhysicalSlice();
|
||||
std::memcpy(slice.mapPtr(0), cDataSlice.ptr(), cDataSlice.length());
|
||||
ctx->invalidateBuffer(cDstBuffer, slice);
|
||||
});
|
||||
D3D11_BUFFER_DESC bufferDesc;
|
||||
pBuffer->GetDesc(&bufferDesc);
|
||||
|
||||
if (bufferDesc.Usage == D3D11_USAGE_DYNAMIC) {
|
||||
EmitCs([
|
||||
cDstBuffer = pBuffer->GetBuffer(),
|
||||
cPhysSlice = pMapEntry->BufferSlice
|
||||
] (DxvkContext* ctx) {
|
||||
ctx->invalidateBuffer(cDstBuffer, cPhysSlice);
|
||||
});
|
||||
} else {
|
||||
EmitCs([
|
||||
cDstBuffer = pBuffer->GetBuffer(),
|
||||
cDataSlice = pMapEntry->DataSlice
|
||||
] (DxvkContext* ctx) {
|
||||
DxvkPhysicalBufferSlice slice = cDstBuffer->allocPhysicalSlice();
|
||||
std::memcpy(slice.mapPtr(0), cDataSlice.ptr(), cDataSlice.length());
|
||||
ctx->invalidateBuffer(cDstBuffer, slice);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,12 +11,14 @@
|
||||
namespace dxvk {
|
||||
|
||||
struct D3D11DeferredContextMapEntry {
|
||||
Com<ID3D11Resource> pResource;
|
||||
UINT Subresource;
|
||||
D3D11_MAP MapType;
|
||||
UINT RowPitch;
|
||||
UINT DepthPitch;
|
||||
DxvkDataSlice DataSlice;
|
||||
Com<ID3D11Resource> pResource;
|
||||
UINT Subresource;
|
||||
D3D11_MAP MapType;
|
||||
UINT RowPitch;
|
||||
UINT DepthPitch;
|
||||
DxvkDataSlice DataSlice;
|
||||
DxvkPhysicalBufferSlice BufferSlice;
|
||||
void* MapPointer;
|
||||
};
|
||||
|
||||
class D3D11DeferredContext : public D3D11DeviceContext {
|
||||
|
@ -182,7 +182,7 @@ namespace dxvk {
|
||||
|
||||
try {
|
||||
const Com<D3D11Texture1D> texture = new D3D11Texture1D(this, &desc);
|
||||
this->InitTexture(texture->GetCommonTexture()->GetImage(), pInitialData);
|
||||
this->InitTexture(texture->GetCommonTexture(), pInitialData);
|
||||
*ppTexture1D = texture.ref();
|
||||
return S_OK;
|
||||
} catch (const DxvkError& e) {
|
||||
@ -219,7 +219,7 @@ namespace dxvk {
|
||||
|
||||
try {
|
||||
const Com<D3D11Texture2D> texture = new D3D11Texture2D(this, &desc);
|
||||
this->InitTexture(texture->GetCommonTexture()->GetImage(), pInitialData);
|
||||
this->InitTexture(texture->GetCommonTexture(), pInitialData);
|
||||
*ppTexture2D = texture.ref();
|
||||
return S_OK;
|
||||
} catch (const DxvkError& e) {
|
||||
@ -256,7 +256,7 @@ namespace dxvk {
|
||||
|
||||
try {
|
||||
const Com<D3D11Texture3D> texture = new D3D11Texture3D(this, &desc);
|
||||
this->InitTexture(texture->GetCommonTexture()->GetImage(), pInitialData);
|
||||
this->InitTexture(texture->GetCommonTexture(), pInitialData);
|
||||
*ppTexture3D = texture.ref();
|
||||
return S_OK;
|
||||
} catch (const DxvkError& e) {
|
||||
@ -413,9 +413,6 @@ namespace dxvk {
|
||||
viewInfo.numLevels = desc.Texture2D.MipLevels;
|
||||
viewInfo.minLayer = 0;
|
||||
viewInfo.numLayers = 1;
|
||||
|
||||
if (m_dxbcOptions.test(DxbcOption::ForceTex2DArray))
|
||||
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
break;
|
||||
|
||||
case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:
|
||||
@ -432,9 +429,6 @@ namespace dxvk {
|
||||
viewInfo.numLevels = 1;
|
||||
viewInfo.minLayer = 0;
|
||||
viewInfo.numLayers = 1;
|
||||
|
||||
if (m_dxbcOptions.test(DxbcOption::ForceTex2DArray))
|
||||
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
break;
|
||||
|
||||
case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:
|
||||
@ -624,9 +618,6 @@ namespace dxvk {
|
||||
viewInfo.numLevels = 1;
|
||||
viewInfo.minLayer = 0;
|
||||
viewInfo.numLayers = 1;
|
||||
|
||||
if (m_dxbcOptions.test(DxbcOption::ForceTex2DArray))
|
||||
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
break;
|
||||
|
||||
case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:
|
||||
@ -1124,7 +1115,10 @@ namespace dxvk {
|
||||
ID3D11GeometryShader** ppGeometryShader) {
|
||||
InitReturnPtr(ppGeometryShader);
|
||||
Logger::err("D3D11Device::CreateGeometryShaderWithStreamOutput: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
|
||||
// Returning S_OK instead of an error fixes some issues
|
||||
// with Overwatch until this is properly implemented
|
||||
return m_d3d11Options.test(D3D11Option::FakeStreamOutSupport) ? S_OK : E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
@ -1813,6 +1807,8 @@ namespace dxvk {
|
||||
enabled.shaderFloat64 = supported.shaderFloat64;
|
||||
enabled.shaderInt64 = supported.shaderInt64;
|
||||
enabled.tessellationShader = VK_TRUE;
|
||||
// TODO enable unconditionally once RADV gains support
|
||||
enabled.shaderStorageImageMultisample = supported.shaderStorageImageMultisample;
|
||||
enabled.shaderStorageImageReadWithoutFormat = supported.shaderStorageImageReadWithoutFormat;
|
||||
enabled.shaderStorageImageWriteWithoutFormat = VK_TRUE;
|
||||
}
|
||||
@ -1849,8 +1845,10 @@ namespace dxvk {
|
||||
void D3D11Device::InitBuffer(
|
||||
D3D11Buffer* pBuffer,
|
||||
const D3D11_SUBRESOURCE_DATA* pInitialData) {
|
||||
const DxvkBufferSlice bufferSlice
|
||||
= pBuffer->GetBufferSlice();
|
||||
const DxvkBufferSlice bufferSlice = pBuffer->GetBufferSlice();
|
||||
|
||||
D3D11_BUFFER_DESC desc;
|
||||
pBuffer->GetDesc(&desc);
|
||||
|
||||
if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) {
|
||||
LockResourceInitContext();
|
||||
@ -1861,14 +1859,25 @@ namespace dxvk {
|
||||
bufferSlice.length(),
|
||||
pInitialData->pSysMem);
|
||||
|
||||
UnlockResourceInitContext(1);
|
||||
} else if (desc.Usage == D3D11_USAGE_DEFAULT) {
|
||||
LockResourceInitContext();
|
||||
|
||||
m_resourceInitContext->clearBuffer(
|
||||
bufferSlice.buffer(),
|
||||
bufferSlice.offset(),
|
||||
bufferSlice.length(),
|
||||
0u);
|
||||
|
||||
UnlockResourceInitContext(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void D3D11Device::InitTexture(
|
||||
const Rc<DxvkImage>& image,
|
||||
D3D11CommonTexture* pTexture,
|
||||
const D3D11_SUBRESOURCE_DATA* pInitialData) {
|
||||
const Rc<DxvkImage> image = pTexture->GetImage();
|
||||
const DxvkFormatInfo* formatInfo = imageFormatInfo(image->info().format);
|
||||
|
||||
if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) {
|
||||
|
@ -19,6 +19,7 @@ namespace dxvk {
|
||||
class DxgiAdapter;
|
||||
|
||||
class D3D11Buffer;
|
||||
class D3D11CommonTexture;
|
||||
class D3D11Counter;
|
||||
class D3D11DeviceContext;
|
||||
class D3D11ImmediateContext;
|
||||
@ -380,7 +381,7 @@ namespace dxvk {
|
||||
const D3D11_SUBRESOURCE_DATA* pInitialData);
|
||||
|
||||
void InitTexture(
|
||||
const Rc<DxvkImage>& image,
|
||||
D3D11CommonTexture* pTexture,
|
||||
const D3D11_SUBRESOURCE_DATA* pInitialData);
|
||||
|
||||
HRESULT GetFormatSupportFlags(
|
||||
|
@ -4,11 +4,11 @@
|
||||
namespace dxvk {
|
||||
|
||||
D3D11InputLayout::D3D11InputLayout(
|
||||
D3D11Device* pDevice,
|
||||
uint32_t numAttributes,
|
||||
const DxvkVertexAttribute* pAttributes,
|
||||
uint32_t numBindings,
|
||||
const DxvkVertexBinding* pBindings)
|
||||
D3D11Device* pDevice,
|
||||
uint32_t numAttributes,
|
||||
const DxvkVertexAttribute* pAttributes,
|
||||
uint32_t numBindings,
|
||||
const DxvkVertexBinding* pBindings)
|
||||
: m_device(pDevice) {
|
||||
m_attributes.resize(numAttributes);
|
||||
m_bindings.resize(numBindings);
|
||||
@ -55,4 +55,25 @@ namespace dxvk {
|
||||
m_bindings.data());
|
||||
}
|
||||
|
||||
|
||||
bool D3D11InputLayout::Compare(const D3D11InputLayout* pOther) const {
|
||||
bool eq = m_attributes.size() == pOther->m_attributes.size()
|
||||
&& m_bindings.size() == pOther->m_bindings.size();
|
||||
|
||||
for (uint32_t i = 0; eq && i < m_attributes.size(); i++) {
|
||||
eq &= m_attributes[i].location == pOther->m_attributes[i].location
|
||||
&& m_attributes[i].binding == pOther->m_attributes[i].binding
|
||||
&& m_attributes[i].format == pOther->m_attributes[i].format
|
||||
&& m_attributes[i].offset == pOther->m_attributes[i].offset;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; eq && i < m_bindings.size(); i++) {
|
||||
eq &= m_bindings[i].binding == pOther->m_bindings[i].binding
|
||||
&& m_bindings[i].fetchRate == pOther->m_bindings[i].fetchRate
|
||||
&& m_bindings[i].inputRate == pOther->m_bindings[i].inputRate;
|
||||
}
|
||||
|
||||
return eq;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,23 +11,26 @@ namespace dxvk {
|
||||
public:
|
||||
|
||||
D3D11InputLayout(
|
||||
D3D11Device* pDevice,
|
||||
uint32_t numAttributes,
|
||||
const DxvkVertexAttribute* pAttributes,
|
||||
uint32_t numBindings,
|
||||
const DxvkVertexBinding* pBindings);
|
||||
D3D11Device* pDevice,
|
||||
uint32_t numAttributes,
|
||||
const DxvkVertexAttribute* pAttributes,
|
||||
uint32_t numBindings,
|
||||
const DxvkVertexBinding* pBindings);
|
||||
|
||||
~D3D11InputLayout();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) final;
|
||||
REFIID riid,
|
||||
void** ppvObject) final;
|
||||
|
||||
void STDMETHODCALLTYPE GetDevice(
|
||||
ID3D11Device **ppDevice) final;
|
||||
ID3D11Device** ppDevice) final;
|
||||
|
||||
void BindToContext(
|
||||
const Rc<DxvkContext>& ctx);
|
||||
const Rc<DxvkContext>& ctx);
|
||||
|
||||
bool Compare(
|
||||
const D3D11InputLayout* pOther) const;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -5,8 +5,9 @@
|
||||
namespace dxvk {
|
||||
|
||||
const static std::unordered_map<std::string, D3D11OptionSet> g_d3d11AppOptions = {{
|
||||
{ "Dishonored2.exe", D3D11OptionSet(D3D11Option::AllowMapFlagNoWait) },
|
||||
{ "Dishonored2.exe", D3D11OptionSet(D3D11Option::AllowMapFlagNoWait) },
|
||||
{ "Fallout4.exe", D3D11OptionSet(D3D11Option::DisableGetDataFlagDoNotFlush) },
|
||||
{ "Overwatch.exe", D3D11OptionSet(D3D11Option::FakeStreamOutSupport) },
|
||||
}};
|
||||
|
||||
|
||||
|
@ -23,6 +23,16 @@ namespace dxvk {
|
||||
* when passing the \c DONOTFLUSH flag.
|
||||
*/
|
||||
DisableGetDataFlagDoNotFlush = 1,
|
||||
|
||||
/**
|
||||
* \brief Fakes stream output support
|
||||
*
|
||||
* Temporary hack that fixes issues in some games
|
||||
* which technically need stream output but work
|
||||
* well enough without it. Will be removed once
|
||||
* Stream Output is properly supported in DXVK.
|
||||
*/
|
||||
FakeStreamOutSupport = 63,
|
||||
};
|
||||
|
||||
using D3D11OptionSet = Flags<D3D11Option>;
|
||||
|
@ -43,15 +43,15 @@ namespace dxvk {
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D11Presenter::CreateSwapChainBackBuffer(
|
||||
const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pSwapChainDesc,
|
||||
IDXGIVkBackBuffer** ppInterface) {
|
||||
D3D11_COMMON_TEXTURE_DESC desc;
|
||||
desc.Width = pSwapChainDesc->BufferDesc.Width;
|
||||
desc.Height = pSwapChainDesc->BufferDesc.Height;
|
||||
desc.Width = pSwapChainDesc->Width;
|
||||
desc.Height = pSwapChainDesc->Height;
|
||||
desc.Depth = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = pSwapChainDesc->BufferDesc.Format;
|
||||
desc.Format = pSwapChainDesc->Format;
|
||||
desc.SampleDesc = pSwapChainDesc->SampleDesc;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_RENDER_TARGET
|
||||
|
@ -54,7 +54,7 @@ namespace dxvk {
|
||||
void** ppvObject);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateSwapChainBackBuffer(
|
||||
const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pSwapChainDesc,
|
||||
IDXGIVkBackBuffer** ppInterface);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE FlushRenderingCommands();
|
||||
|
@ -34,7 +34,17 @@ namespace dxvk {
|
||||
m_analysis->uavInfos[registerId].accessAtomicOp = true;
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
case DxbcInstClass::TextureSample:
|
||||
case DxbcInstClass::VectorDeriv: {
|
||||
m_analysis->usesDerivatives = true;
|
||||
} break;
|
||||
|
||||
case DxbcInstClass::ControlFlow: {
|
||||
if (ins.op == DxbcOpcode::Discard)
|
||||
m_analysis->usesKill = true;
|
||||
} break;
|
||||
|
||||
case DxbcInstClass::TypedUavLoad: {
|
||||
const uint32_t registerId = ins.src[1].idx[0].offset;
|
||||
m_analysis->uavInfos[registerId].accessTypedLoad = true;
|
||||
|
@ -37,6 +37,9 @@ namespace dxvk {
|
||||
|
||||
DxbcClipCullInfo clipCullIn;
|
||||
DxbcClipCullInfo clipCullOut;
|
||||
|
||||
bool usesDerivatives = false;
|
||||
bool usesKill = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,13 @@ namespace dxvk {
|
||||
m_oRegs.at(i) = 0;
|
||||
}
|
||||
|
||||
// Clear spec constants
|
||||
for (uint32_t i = 0; i < m_specConstants.size(); i++) {
|
||||
m_specConstants.at(i) = DxbcRegisterValue {
|
||||
DxbcVectorType { DxbcScalarType::Uint32, 0 },
|
||||
0 };
|
||||
}
|
||||
|
||||
this->emitInit();
|
||||
}
|
||||
|
||||
@ -277,6 +284,9 @@ namespace dxvk {
|
||||
case DxbcOpcode::DclThreadGroup:
|
||||
return this->emitDclThreadGroup(ins);
|
||||
|
||||
case DxbcOpcode::DclGsInstanceCount:
|
||||
return this->emitDclGsInstanceCount(ins);
|
||||
|
||||
default:
|
||||
Logger::warn(
|
||||
str::format("DxbcCompiler: Unhandled opcode: ",
|
||||
@ -456,7 +466,6 @@ namespace dxvk {
|
||||
} break;
|
||||
|
||||
case DxbcOperandType::InputCoverageMask: {
|
||||
m_module.enableCapability(spv::CapabilitySampleRateShading);
|
||||
m_ps.builtinSampleMaskIn = emitNewBuiltinVariable({
|
||||
{ DxbcScalarType::Uint32, 1, 1 },
|
||||
spv::StorageClassInput },
|
||||
@ -465,7 +474,6 @@ namespace dxvk {
|
||||
} break;
|
||||
|
||||
case DxbcOperandType::OutputCoverageMask: {
|
||||
m_module.enableCapability(spv::CapabilitySampleRateShading);
|
||||
m_ps.builtinSampleMaskOut = emitNewBuiltinVariable({
|
||||
{ DxbcScalarType::Uint32, 1, 1 },
|
||||
spv::StorageClassOutput },
|
||||
@ -547,6 +555,14 @@ namespace dxvk {
|
||||
// output arrays, so there's nothing left to do.
|
||||
} break;
|
||||
|
||||
case DxbcOperandType::InputGsInstanceId: {
|
||||
m_gs.builtinInvocationId = emitNewBuiltinVariable({
|
||||
{ DxbcScalarType::Uint32, 1, 0 },
|
||||
spv::StorageClassInput },
|
||||
spv::BuiltInInvocationId,
|
||||
"vInstanceID");
|
||||
} break;
|
||||
|
||||
default:
|
||||
Logger::err(str::format(
|
||||
"DxbcCompiler: Unsupported operand type declaration: ",
|
||||
@ -819,7 +835,6 @@ namespace dxvk {
|
||||
case DxbcResourceDim::Buffer: m_module.enableCapability(spv::CapabilityImageBuffer); break;
|
||||
case DxbcResourceDim::Texture1D: m_module.enableCapability(spv::CapabilityImage1D); break;
|
||||
case DxbcResourceDim::Texture1DArr: m_module.enableCapability(spv::CapabilityImage1D); break;
|
||||
case DxbcResourceDim::TextureCube: m_module.enableCapability(spv::CapabilityImageCubeArray); break;
|
||||
case DxbcResourceDim::TextureCubeArr: m_module.enableCapability(spv::CapabilityImageCubeArray); break;
|
||||
case DxbcResourceDim::Texture2DMs: m_module.enableCapability(spv::CapabilityImageMSArray); break;
|
||||
case DxbcResourceDim::Texture2DMsArr: m_module.enableCapability(spv::CapabilityImageMSArray); break;
|
||||
@ -955,7 +970,7 @@ namespace dxvk {
|
||||
const DxbcScalarType sampledType = DxbcScalarType::Uint32;
|
||||
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
|
||||
|
||||
const DxbcImageInfo typeInfo = { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_MAX_ENUM };
|
||||
const DxbcImageInfo typeInfo = { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM };
|
||||
|
||||
// Declare the resource type
|
||||
const uint32_t resTypeId = m_module.defImageType(sampledTypeId,
|
||||
@ -1221,6 +1236,13 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitDclGsInstanceCount(const DxbcShaderInstruction& ins) {
|
||||
// dcl_gs_instance_count has one operand:
|
||||
// (imm0) Number of geometry shader invocations
|
||||
m_module.setInvocations(m_entryPointId, ins.imm[0].u32);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::emitDclUavCounter(uint32_t regId) {
|
||||
// Declare a structure type which holds the UAV counter
|
||||
if (m_uavCtrStructType == 0) {
|
||||
@ -1981,9 +2003,27 @@ namespace dxvk {
|
||||
// (dst0) Register that receives the result
|
||||
// (dst1) Destination u# or g# register
|
||||
// (srcX) As above
|
||||
const DxbcBufferInfo bufferInfo = getBufferInfo(ins.dst[ins.dstCount - 1]);
|
||||
|
||||
const bool isImm = ins.dstCount == 2;
|
||||
const bool isUav = ins.dst[ins.dstCount - 1].type == DxbcOperandType::UnorderedAccessView;
|
||||
|
||||
// Perform atomic operations on UAVs only if the UAV
|
||||
// is bound and if there is nothing else stopping us.
|
||||
DxbcConditional cond;
|
||||
|
||||
if (isUav) {
|
||||
uint32_t writeTest = emitUavWriteTest(bufferInfo);
|
||||
|
||||
cond.labelIf = m_module.allocateId();
|
||||
cond.labelEnd = m_module.allocateId();
|
||||
|
||||
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
||||
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
||||
|
||||
m_module.opLabel(cond.labelIf);
|
||||
}
|
||||
|
||||
// Retrieve destination pointer for the atomic operation>
|
||||
const DxbcRegisterPointer pointer = emitGetAtomicPointer(
|
||||
ins.dst[ins.dstCount - 1], ins.src[0]);
|
||||
@ -2104,6 +2144,12 @@ namespace dxvk {
|
||||
// register if this is an imm_atomic_* opcode.
|
||||
if (isImm)
|
||||
emitRegisterStore(ins.dst[0], value);
|
||||
|
||||
// End conditional block
|
||||
if (isUav) {
|
||||
m_module.opBranch(cond.labelEnd);
|
||||
m_module.opLabel (cond.labelEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2111,12 +2157,25 @@ namespace dxvk {
|
||||
// imm_atomic_alloc and imm_atomic_consume have the following operands:
|
||||
// (dst0) The register that will hold the old counter value
|
||||
// (dst1) The UAV whose counter is going to be modified
|
||||
// TODO check if the corresponding UAV is bound
|
||||
const DxbcBufferInfo bufferInfo = getBufferInfo(ins.dst[1]);
|
||||
|
||||
const uint32_t registerId = ins.dst[1].idx[0].offset;
|
||||
|
||||
if (m_uavs.at(registerId).ctrId == 0)
|
||||
m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId);
|
||||
|
||||
// Only perform the operation if the UAV is bound
|
||||
uint32_t writeTest = emitUavWriteTest(bufferInfo);
|
||||
|
||||
DxbcConditional cond;
|
||||
cond.labelIf = m_module.allocateId();
|
||||
cond.labelEnd = m_module.allocateId();
|
||||
|
||||
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
||||
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
||||
|
||||
m_module.opLabel(cond.labelIf);
|
||||
|
||||
// Get a pointer to the atomic counter in question
|
||||
DxbcRegisterInfo ptrType;
|
||||
ptrType.type.ctype = DxbcScalarType::Uint32;
|
||||
@ -2165,6 +2224,10 @@ namespace dxvk {
|
||||
|
||||
// Store the result
|
||||
emitRegisterStore(ins.dst[0], value);
|
||||
|
||||
// End conditional block
|
||||
m_module.opBranch(cond.labelEnd);
|
||||
m_module.opLabel (cond.labelEnd);
|
||||
}
|
||||
|
||||
|
||||
@ -3232,23 +3295,37 @@ namespace dxvk {
|
||||
// (dst0) The destination UAV
|
||||
// (src0) The texture or buffer coordinates
|
||||
// (src1) The value to store
|
||||
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
||||
const DxbcUav uavInfo = m_uavs.at(registerId);
|
||||
const DxbcBufferInfo uavInfo = getBufferInfo(ins.dst[0]);
|
||||
|
||||
// Execute write op only if the UAV is bound
|
||||
uint32_t writeTest = emitUavWriteTest(uavInfo);
|
||||
|
||||
DxbcConditional cond;
|
||||
cond.labelIf = m_module.allocateId();
|
||||
cond.labelEnd = m_module.allocateId();
|
||||
|
||||
m_module.opSelectionMerge (cond.labelEnd, spv::SelectionControlMaskNone);
|
||||
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
||||
|
||||
m_module.opLabel(cond.labelIf);
|
||||
|
||||
// Load texture coordinates
|
||||
DxbcRegisterValue texCoord = emitLoadTexCoord(
|
||||
ins.src[0], uavInfo.imageInfo);
|
||||
DxbcRegisterValue texCoord = emitLoadTexCoord(ins.src[0], uavInfo.image);
|
||||
|
||||
// Load the value that will be written to the image. We'll
|
||||
// have to cast it to the component type of the image.
|
||||
const DxbcRegisterValue texValue = emitRegisterBitcast(
|
||||
emitRegisterLoad(ins.src[1], DxbcRegMask(true, true, true, true)),
|
||||
uavInfo.sampledType);
|
||||
uavInfo.stype);
|
||||
|
||||
// Write the given value to the image
|
||||
m_module.opImageWrite(
|
||||
m_module.opLoad(uavInfo.imageTypeId, uavInfo.varId),
|
||||
m_module.opLoad(uavInfo.typeId, uavInfo.varId),
|
||||
texCoord.id, texValue.id, SpirvImageOperands());
|
||||
|
||||
// End conditional block
|
||||
m_module.opBranch(cond.labelEnd);
|
||||
m_module.opLabel (cond.labelEnd);
|
||||
}
|
||||
|
||||
|
||||
@ -3258,29 +3335,21 @@ namespace dxvk {
|
||||
const DxbcRegisterValue condition = emitRegisterLoad(
|
||||
ins.src[0], DxbcRegMask(true, false, false, false));
|
||||
|
||||
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
||||
condition, ins.controls.zeroTest());
|
||||
|
||||
// Declare the 'if' block. We do not know if there
|
||||
// will be an 'else' block or not, so we'll assume
|
||||
// that there is one and leave it empty otherwise.
|
||||
DxbcCfgBlock block;
|
||||
block.type = DxbcCfgBlockType::If;
|
||||
block.b_if.ztestId = emitRegisterZeroTest(condition, ins.controls.zeroTest()).id;
|
||||
block.b_if.labelIf = m_module.allocateId();
|
||||
block.b_if.labelElse = m_module.allocateId();
|
||||
block.b_if.labelElse = 0;
|
||||
block.b_if.labelEnd = m_module.allocateId();
|
||||
block.b_if.hadElse = false;
|
||||
block.b_if.headerPtr = m_module.getInsertionPtr();
|
||||
m_controlFlowBlocks.push_back(block);
|
||||
|
||||
m_module.opSelectionMerge(
|
||||
block.b_if.labelEnd,
|
||||
spv::SelectionControlMaskNone);
|
||||
|
||||
m_module.opBranchConditional(
|
||||
zeroTest.id,
|
||||
block.b_if.labelIf,
|
||||
block.b_if.labelElse);
|
||||
|
||||
// We'll insert the branch instruction when closing
|
||||
// the block, since we don't know whether or not an
|
||||
// else block is needed right now.
|
||||
m_module.opLabel(block.b_if.labelIf);
|
||||
}
|
||||
|
||||
@ -3288,13 +3357,13 @@ namespace dxvk {
|
||||
void DxbcCompiler::emitControlFlowElse(const DxbcShaderInstruction& ins) {
|
||||
if (m_controlFlowBlocks.size() == 0
|
||||
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If
|
||||
|| m_controlFlowBlocks.back().b_if.hadElse)
|
||||
|| m_controlFlowBlocks.back().b_if.labelElse != 0)
|
||||
throw DxvkError("DxbcCompiler: 'Else' without 'If' found");
|
||||
|
||||
// Set the 'Else' flag so that we do
|
||||
// not insert a dummy block on 'EndIf'
|
||||
DxbcCfgBlock& block = m_controlFlowBlocks.back();
|
||||
block.b_if.hadElse = true;
|
||||
block.b_if.labelElse = m_module.allocateId();
|
||||
|
||||
// Close the 'If' block by branching to
|
||||
// the merge block we declared earlier
|
||||
@ -3309,21 +3378,28 @@ namespace dxvk {
|
||||
throw DxvkError("DxbcCompiler: 'EndIf' without 'If' found");
|
||||
|
||||
// Remove the block from the stack, it's closed
|
||||
const DxbcCfgBlock block = m_controlFlowBlocks.back();
|
||||
DxbcCfgBlock block = m_controlFlowBlocks.back();
|
||||
m_controlFlowBlocks.pop_back();
|
||||
|
||||
// Write out the 'if' header
|
||||
m_module.beginInsertion(block.b_if.headerPtr);
|
||||
|
||||
m_module.opSelectionMerge(
|
||||
block.b_if.labelEnd,
|
||||
spv::SelectionControlMaskNone);
|
||||
|
||||
m_module.opBranchConditional(
|
||||
block.b_if.ztestId,
|
||||
block.b_if.labelIf,
|
||||
block.b_if.labelElse != 0
|
||||
? block.b_if.labelElse
|
||||
: block.b_if.labelEnd);
|
||||
|
||||
m_module.endInsertion();
|
||||
|
||||
// End the active 'if' or 'else' block
|
||||
m_module.opBranch(block.b_if.labelEnd);
|
||||
|
||||
// If there was no 'else' block in this construct, we still
|
||||
// have to declare it because we used it as a branch target.
|
||||
if (!block.b_if.hadElse) {
|
||||
m_module.opLabel (block.b_if.labelElse);
|
||||
m_module.opBranch(block.b_if.labelEnd);
|
||||
}
|
||||
|
||||
// Declare the merge block
|
||||
m_module.opLabel(block.b_if.labelEnd);
|
||||
m_module.opLabel (block.b_if.labelEnd);
|
||||
}
|
||||
|
||||
|
||||
@ -3589,21 +3665,26 @@ namespace dxvk {
|
||||
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
||||
condition, ins.controls.zeroTest());
|
||||
|
||||
// Insert a Pseudo-'If' block
|
||||
const uint32_t discardBlock = m_module.allocateId();
|
||||
const uint32_t mergeBlock = m_module.allocateId();
|
||||
|
||||
m_module.opSelectionMerge(mergeBlock,
|
||||
spv::SelectionControlMaskNone);
|
||||
|
||||
m_module.opBranchConditional(
|
||||
zeroTest.id, discardBlock, mergeBlock);
|
||||
|
||||
// OpKill terminates the block
|
||||
m_module.opLabel(discardBlock);
|
||||
m_module.opKill();
|
||||
|
||||
m_module.opLabel(mergeBlock);
|
||||
if (m_ps.killState == 0) {
|
||||
DxbcConditional cond;
|
||||
cond.labelIf = m_module.allocateId();
|
||||
cond.labelEnd = m_module.allocateId();
|
||||
|
||||
m_module.opSelectionMerge(cond.labelIf, spv::SelectionControlMaskNone);
|
||||
m_module.opBranchConditional(zeroTest.id, cond.labelIf, cond.labelEnd);
|
||||
|
||||
// OpKill terminates the block
|
||||
m_module.opLabel(cond.labelIf);
|
||||
m_module.opKill();
|
||||
|
||||
m_module.opLabel(cond.labelEnd);
|
||||
} else {
|
||||
uint32_t typeId = m_module.defBoolType();
|
||||
|
||||
uint32_t killState = m_module.opLoad (typeId, m_ps.killState);
|
||||
killState = m_module.opLogicalOr(typeId, killState, zeroTest.id);
|
||||
m_module.opStore(m_ps.killState, killState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4285,6 +4366,11 @@ namespace dxvk {
|
||||
return DxbcRegisterPointer {
|
||||
{ DxbcScalarType::Uint32, 1 },
|
||||
getCurrentHsForkJoinPhase()->instanceIdPtr };
|
||||
|
||||
case DxbcOperandType::InputGsInstanceId:
|
||||
return DxbcRegisterPointer {
|
||||
{ DxbcScalarType::Uint32, 1 },
|
||||
m_gs.builtinInvocationId };
|
||||
|
||||
default:
|
||||
throw DxvkError(str::format(
|
||||
@ -4441,11 +4527,27 @@ namespace dxvk {
|
||||
// Cast source value to the expected data type
|
||||
value = emitRegisterBitcast(value, DxbcScalarType::Uint32);
|
||||
|
||||
// Shared memory is not accessed through a texel buffer view
|
||||
const bool isTgsm = operand.type == DxbcOperandType::ThreadGroupSharedMemory;
|
||||
// Thread Group Shared Memory is not accessed through a texel buffer view
|
||||
const bool isUav = operand.type == DxbcOperandType::UnorderedAccessView;
|
||||
|
||||
const uint32_t bufferId = isTgsm
|
||||
? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId);
|
||||
// Perform UAV writes only if the UAV is bound and if there
|
||||
// is nothing else preventing us from writing to it.
|
||||
DxbcConditional cond;
|
||||
|
||||
if (isUav) {
|
||||
uint32_t writeTest = emitUavWriteTest(bufferInfo);
|
||||
|
||||
cond.labelIf = m_module.allocateId();
|
||||
cond.labelEnd = m_module.allocateId();
|
||||
|
||||
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
||||
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
||||
|
||||
m_module.opLabel(cond.labelIf);
|
||||
}
|
||||
|
||||
// Perform the actual write operation
|
||||
const uint32_t bufferId = isUav ? m_module.opLoad(bufferInfo.typeId, bufferInfo.varId) : 0;
|
||||
|
||||
const uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 });
|
||||
const uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 });
|
||||
@ -4494,6 +4596,12 @@ namespace dxvk {
|
||||
srcComponentIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// End conditional block
|
||||
if (isUav) {
|
||||
m_module.opBranch(cond.labelEnd);
|
||||
m_module.opLabel (cond.labelEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4538,15 +4646,21 @@ namespace dxvk {
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::emitQueryTextureSamples(
|
||||
const DxbcRegister& resource) {
|
||||
const DxbcBufferInfo info = getBufferInfo(resource);
|
||||
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 1;
|
||||
result.id = m_module.opImageQuerySamples(
|
||||
getVectorTypeId(result.type),
|
||||
m_module.opLoad(info.typeId, info.varId));
|
||||
return result;
|
||||
if (resource.type == DxbcOperandType::Rasterizer) {
|
||||
// SPIR-V has no gl_NumSamples equivalent, so we have
|
||||
// to work around it using a specialization constant
|
||||
return getSpecConstant(DxvkSpecConstantId::RasterizerSampleCount);
|
||||
} else {
|
||||
DxbcBufferInfo info = getBufferInfo(resource);
|
||||
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 1;
|
||||
result.id = m_module.opImageQuerySamples(
|
||||
getVectorTypeId(result.type),
|
||||
m_module.opLoad(info.typeId, info.varId));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4570,15 +4684,6 @@ namespace dxvk {
|
||||
m_module.opLoad(info.typeId, info.varId));
|
||||
}
|
||||
|
||||
if (info.image.array && !info.image.layered) {
|
||||
const uint32_t index = result.type.ccount - 1;
|
||||
const uint32_t zero = m_module.constu32(0);
|
||||
|
||||
result.id = m_module.opCompositeInsert(
|
||||
getVectorTypeId(result.type),
|
||||
zero, result.id, 1, &index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -4623,22 +4728,6 @@ namespace dxvk {
|
||||
coordVector, DxbcRegMask::firstN(dim));
|
||||
}
|
||||
|
||||
if (imageInfo.array && !imageInfo.layered) {
|
||||
const uint32_t index = dim - 1;
|
||||
const uint32_t zero = [&] {
|
||||
switch (coordVector.type.ctype) {
|
||||
case DxbcScalarType::Sint32: return m_module.consti32(0);
|
||||
case DxbcScalarType::Uint32: return m_module.constu32(0);
|
||||
case DxbcScalarType::Float32: return m_module.constf32(0.0f);
|
||||
default: throw DxvkError("Dxbc: Invalid tex coord type");
|
||||
}
|
||||
}();
|
||||
|
||||
coordVector.id = m_module.opCompositeInsert(
|
||||
getVectorTypeId(coordVector.type),
|
||||
zero, coordVector.id, 1, &index);
|
||||
}
|
||||
|
||||
return coordVector;
|
||||
}
|
||||
|
||||
@ -4784,6 +4873,43 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::getSpecConstant(DxvkSpecConstantId specId) {
|
||||
const uint32_t specIdOffset = uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin);
|
||||
|
||||
// Look up spec constant in the array
|
||||
DxbcRegisterValue value = m_specConstants.at(specIdOffset);
|
||||
|
||||
if (value.id != 0)
|
||||
return value;
|
||||
|
||||
// Declare a new specialization constant if needed
|
||||
DxbcSpecConstant info = getSpecConstantProperties(specId);
|
||||
|
||||
value.type.ctype = info.ctype;
|
||||
value.type.ccount = info.ccount;
|
||||
value.id = m_module.specConst32(
|
||||
getVectorTypeId(value.type),
|
||||
info.value);
|
||||
|
||||
m_module.decorateSpecId(value.id, uint32_t(specId));
|
||||
m_module.setDebugName(value.id, info.name);
|
||||
|
||||
m_specConstants.at(specIdOffset) = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcSpecConstant DxbcCompiler::getSpecConstantProperties(DxvkSpecConstantId specId) {
|
||||
static const std::array<DxbcSpecConstant,
|
||||
uint32_t(DxvkSpecConstantId::SpecConstantIdMax) -
|
||||
uint32_t(DxvkSpecConstantId::SpecConstantIdMin) + 1> s_specConstants = {{
|
||||
{ DxbcScalarType::Uint32, 1, 1, "RasterizerSampleCount" },
|
||||
}};
|
||||
|
||||
return s_specConstants.at(uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin));
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitInputSetup() {
|
||||
// Copy all defined v# registers into the input array
|
||||
const uint32_t vecTypeId = m_module.defVectorType(m_module.defFloatType(32), 4);
|
||||
@ -5429,6 +5555,21 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::emitUavWriteTest(const DxbcBufferInfo& uav) {
|
||||
uint32_t typeId = m_module.defBoolType();
|
||||
uint32_t testId = uav.specId;
|
||||
|
||||
if (m_ps.killState != 0) {
|
||||
uint32_t killState = m_module.opLoad(typeId, m_ps.killState);
|
||||
|
||||
testId = m_module.opLogicalAnd(typeId, testId,
|
||||
m_module.opLogicalNot(typeId, killState));
|
||||
}
|
||||
|
||||
return testId;
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitInit() {
|
||||
// Set up common capabilities for all shaders
|
||||
m_module.enableCapability(spv::CapabilityShader);
|
||||
@ -5633,6 +5774,16 @@ namespace dxvk {
|
||||
spv::BuiltInCullDistance,
|
||||
spv::StorageClassInput);
|
||||
|
||||
// We may have to defer kill operations to the end of
|
||||
// the shader in order to keep derivatives correct.
|
||||
if (m_analysis->usesKill && m_analysis->usesDerivatives && m_options.test(DxbcOption::DeferKill)) {
|
||||
m_ps.killState = m_module.newVarInit(
|
||||
m_module.defPointerType(m_module.defBoolType(), spv::StorageClassPrivate),
|
||||
spv::StorageClassPrivate, m_module.constBool(false));
|
||||
|
||||
m_module.setDebugName(m_ps.killState, "ps_kill");
|
||||
}
|
||||
|
||||
// Main function of the pixel shader
|
||||
m_ps.functionId = m_module.allocateId();
|
||||
m_module.setDebugName(m_ps.functionId, "ps_main");
|
||||
@ -5730,9 +5881,27 @@ namespace dxvk {
|
||||
this->emitInputSetup();
|
||||
this->emitClipCullLoad(DxbcSystemValue::ClipDistance, m_clipDistances);
|
||||
this->emitClipCullLoad(DxbcSystemValue::CullDistance, m_cullDistances);
|
||||
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_ps.functionId, 0, nullptr);
|
||||
|
||||
if (m_ps.killState != 0) {
|
||||
DxbcConditional cond;
|
||||
cond.labelIf = m_module.allocateId();
|
||||
cond.labelEnd = m_module.allocateId();
|
||||
|
||||
uint32_t killTest = m_module.opLoad(m_module.defBoolType(), m_ps.killState);
|
||||
|
||||
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
||||
m_module.opBranchConditional(killTest, cond.labelIf, cond.labelEnd);
|
||||
|
||||
m_module.opLabel(cond.labelIf);
|
||||
m_module.opKill();
|
||||
|
||||
m_module.opLabel(cond.labelEnd);
|
||||
}
|
||||
|
||||
this->emitOutputSetup();
|
||||
this->emitMainFunctionEnd();
|
||||
}
|
||||
@ -6256,25 +6425,20 @@ namespace dxvk {
|
||||
bool isUav) const {
|
||||
DxbcImageInfo typeInfo = [resourceType, isUav] () -> DxbcImageInfo {
|
||||
switch (resourceType) {
|
||||
case DxbcResourceDim::Buffer: return { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_MAX_ENUM };
|
||||
case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_1D };
|
||||
case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_1D_ARRAY };
|
||||
case DxbcResourceDim::Texture2D: return { spv::Dim2D, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_2D };
|
||||
case DxbcResourceDim::Texture2DArr: return { spv::Dim2D, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
||||
case DxbcResourceDim::Texture2DMs: return { spv::Dim2D, 0, 1, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_2D };
|
||||
case DxbcResourceDim::Texture2DMsArr: return { spv::Dim2D, 1, 1, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
||||
case DxbcResourceDim::Texture3D: return { spv::Dim3D, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_3D };
|
||||
case DxbcResourceDim::TextureCube: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY };
|
||||
case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY };
|
||||
case DxbcResourceDim::Buffer: return { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM };
|
||||
case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_1D };
|
||||
case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_1D_ARRAY };
|
||||
case DxbcResourceDim::Texture2D: return { spv::Dim2D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D };
|
||||
case DxbcResourceDim::Texture2DArr: return { spv::Dim2D, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
||||
case DxbcResourceDim::Texture2DMs: return { spv::Dim2D, 0, 1, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D };
|
||||
case DxbcResourceDim::Texture2DMsArr: return { spv::Dim2D, 1, 1, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
||||
case DxbcResourceDim::Texture3D: return { spv::Dim3D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_3D };
|
||||
case DxbcResourceDim::TextureCube: return { spv::DimCube, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_CUBE };
|
||||
case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY };
|
||||
default: throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType));
|
||||
}
|
||||
}();
|
||||
|
||||
if (typeInfo.dim == spv::Dim2D && m_options.test(DxbcOption::ForceTex2DArray)) {
|
||||
typeInfo.array = 1;
|
||||
typeInfo.vtype = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
}
|
||||
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,34 @@ namespace dxvk {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Specialization constant properties
|
||||
*
|
||||
* Stores the name, data type and initial
|
||||
* value of a specialization constant.
|
||||
*/
|
||||
struct DxbcSpecConstant {
|
||||
DxbcScalarType ctype;
|
||||
uint32_t ccount;
|
||||
uint32_t value;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Helper struct for conditional execution
|
||||
*
|
||||
* Stores a set of labels required to implement either
|
||||
* an if-then block or an if-then-else block. This is
|
||||
* not used to implement control flow instructions.
|
||||
*/
|
||||
struct DxbcConditional {
|
||||
uint32_t labelIf = 0;
|
||||
uint32_t labelElse = 0;
|
||||
uint32_t labelEnd = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Vertex shader-specific structure
|
||||
*/
|
||||
@ -120,6 +148,7 @@ namespace dxvk {
|
||||
|
||||
uint32_t builtinLayer = 0;
|
||||
uint32_t builtinViewportId = 0;
|
||||
uint32_t builtinInvocationId = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -136,6 +165,8 @@ namespace dxvk {
|
||||
uint32_t builtinSampleMaskIn = 0;
|
||||
uint32_t builtinSampleMaskOut = 0;
|
||||
uint32_t builtinLayer = 0;
|
||||
|
||||
uint32_t killState = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -244,10 +275,11 @@ namespace dxvk {
|
||||
|
||||
|
||||
struct DxbcCfgBlockIf {
|
||||
uint32_t ztestId;
|
||||
uint32_t labelIf;
|
||||
uint32_t labelElse;
|
||||
uint32_t labelEnd;
|
||||
bool hadElse;
|
||||
size_t headerPtr;
|
||||
};
|
||||
|
||||
|
||||
@ -382,6 +414,13 @@ namespace dxvk {
|
||||
// currently active if-else blocks and loops.
|
||||
std::vector<DxbcCfgBlock> m_controlFlowBlocks;
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// Specialization constants. These are defined
|
||||
// as needed by the getSpecConstant method.
|
||||
std::array<DxbcRegisterValue,
|
||||
uint32_t(DxvkSpecConstantId::SpecConstantIdMax) -
|
||||
uint32_t(DxvkSpecConstantId::SpecConstantIdMin) + 1> m_specConstants;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Array of input values. Since v# registers are indexable
|
||||
// in DXBC, we need to copy them into an array first.
|
||||
@ -513,6 +552,9 @@ namespace dxvk {
|
||||
void emitDclThreadGroup(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
void emitDclGsInstanceCount(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
uint32_t emitDclUavCounter(
|
||||
uint32_t regId);
|
||||
|
||||
@ -843,6 +885,14 @@ namespace dxvk {
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegisterValue value);
|
||||
|
||||
////////////////////////////////////////
|
||||
// Spec constant declaration and access
|
||||
DxbcRegisterValue getSpecConstant(
|
||||
DxvkSpecConstantId specId);
|
||||
|
||||
DxbcSpecConstant getSpecConstantProperties(
|
||||
DxvkSpecConstantId specId);
|
||||
|
||||
////////////////////////////
|
||||
// Input/output preparation
|
||||
void emitInputSetup();
|
||||
@ -906,6 +956,11 @@ namespace dxvk {
|
||||
DxbcSystemValue sv,
|
||||
uint32_t srcArray);
|
||||
|
||||
///////////////////////////////
|
||||
// Some state checking methods
|
||||
uint32_t emitUavWriteTest(
|
||||
const DxbcBufferInfo& uav);
|
||||
|
||||
//////////////////////////////////////
|
||||
// Common function definition methods
|
||||
void emitInit();
|
||||
|
@ -60,7 +60,6 @@ namespace dxvk {
|
||||
uint32_t array = 0;
|
||||
uint32_t ms = 0;
|
||||
uint32_t sampled = 0;
|
||||
uint32_t layered = 0;
|
||||
VkImageViewType vtype = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
||||
};
|
||||
|
||||
|
@ -999,7 +999,9 @@ namespace dxvk {
|
||||
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
|
||||
} },
|
||||
/* DclGsInstanceCount */
|
||||
{ },
|
||||
{ 1, DxbcInstClass::Declaration, {
|
||||
{ DxbcOperandKind::Imm32, DxbcScalarType::Uint32 },
|
||||
} },
|
||||
}};
|
||||
|
||||
|
||||
|
@ -54,13 +54,14 @@ namespace dxvk {
|
||||
m_isgnChunk, m_osgnChunk,
|
||||
analysisInfo);
|
||||
|
||||
this->runAnalyzer(analyzer, m_shexChunk->slice());
|
||||
|
||||
DxbcCompiler compiler(
|
||||
fileName, options,
|
||||
m_shexChunk->version(),
|
||||
m_isgnChunk, m_osgnChunk,
|
||||
analysisInfo);
|
||||
|
||||
this->runAnalyzer(analyzer, m_shexChunk->slice());
|
||||
this->runCompiler(compiler, m_shexChunk->slice());
|
||||
|
||||
return compiler.finalize();
|
||||
|
@ -5,8 +5,7 @@
|
||||
namespace dxvk {
|
||||
|
||||
const static std::unordered_map<std::string, DxbcOptions> g_dxbcAppOptions = {{
|
||||
{ "Dishonored2.exe", DxbcOptions(DxbcOption::ForceTex2DArray) },
|
||||
{ "ManiaPlanet.exe", DxbcOptions(DxbcOption::ForceTex2DArray) },
|
||||
|
||||
}};
|
||||
|
||||
|
||||
@ -36,6 +35,7 @@ namespace dxvk {
|
||||
if (devFeatures.shaderStorageImageReadWithoutFormat)
|
||||
flags.set(DxbcOption::UseStorageImageReadWithoutFormat);
|
||||
|
||||
flags.set(DxbcOption::DeferKill);
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,10 @@ namespace dxvk {
|
||||
/// Workaround for bugs in older Nvidia drivers.
|
||||
UseSimpleMinMaxClamp,
|
||||
|
||||
/// Enforces the use of array views even when dealing
|
||||
/// with non-array texture types. Some games do not
|
||||
/// bind the correct texture type to the pipeline.
|
||||
ForceTex2DArray,
|
||||
/// Defer kill operation to the end of the shader.
|
||||
/// Fixes derivatives that are undefined due to
|
||||
/// non-uniform control flow in fragment shaders.
|
||||
DeferKill,
|
||||
};
|
||||
|
||||
using DxbcOptions = Flags<DxbcOption>;
|
||||
|
@ -36,6 +36,7 @@ namespace dxvk {
|
||||
|| riid == __uuidof(IDXGIObject)
|
||||
|| riid == __uuidof(IDXGIAdapter)
|
||||
|| riid == __uuidof(IDXGIAdapter1)
|
||||
|| riid == __uuidof(IDXGIAdapter2)
|
||||
|| riid == __uuidof(IDXGIVkAdapter)) {
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
@ -96,23 +97,20 @@ namespace dxvk {
|
||||
if (pDesc == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
DXGI_ADAPTER_DESC1 desc1;
|
||||
HRESULT hr = this->GetDesc1(&desc1);
|
||||
DXGI_ADAPTER_DESC2 desc;
|
||||
HRESULT hr = GetDesc2(&desc);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::memcpy(
|
||||
pDesc->Description,
|
||||
desc1.Description,
|
||||
sizeof(pDesc->Description));
|
||||
std::memcpy(pDesc->Description, desc.Description, sizeof(pDesc->Description));
|
||||
|
||||
pDesc->VendorId = desc1.VendorId;
|
||||
pDesc->DeviceId = desc1.DeviceId;
|
||||
pDesc->SubSysId = desc1.SubSysId;
|
||||
pDesc->Revision = desc1.Revision;
|
||||
pDesc->DedicatedVideoMemory = desc1.DedicatedVideoMemory;
|
||||
pDesc->DedicatedSystemMemory = desc1.DedicatedSystemMemory;
|
||||
pDesc->SharedSystemMemory = desc1.SharedSystemMemory;
|
||||
pDesc->AdapterLuid = desc1.AdapterLuid;
|
||||
pDesc->VendorId = desc.VendorId;
|
||||
pDesc->DeviceId = desc.DeviceId;
|
||||
pDesc->SubSysId = desc.SubSysId;
|
||||
pDesc->Revision = desc.Revision;
|
||||
pDesc->DedicatedVideoMemory = desc.DedicatedVideoMemory;
|
||||
pDesc->DedicatedSystemMemory = desc.DedicatedSystemMemory;
|
||||
pDesc->SharedSystemMemory = desc.SharedSystemMemory;
|
||||
pDesc->AdapterLuid = desc.AdapterLuid;
|
||||
}
|
||||
|
||||
return hr;
|
||||
@ -122,6 +120,31 @@ namespace dxvk {
|
||||
HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc1(DXGI_ADAPTER_DESC1* pDesc) {
|
||||
if (pDesc == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
DXGI_ADAPTER_DESC2 desc;
|
||||
HRESULT hr = GetDesc2(&desc);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::memcpy(pDesc->Description, desc.Description, sizeof(pDesc->Description));
|
||||
|
||||
pDesc->VendorId = desc.VendorId;
|
||||
pDesc->DeviceId = desc.DeviceId;
|
||||
pDesc->SubSysId = desc.SubSysId;
|
||||
pDesc->Revision = desc.Revision;
|
||||
pDesc->DedicatedVideoMemory = desc.DedicatedVideoMemory;
|
||||
pDesc->DedicatedSystemMemory = desc.DedicatedSystemMemory;
|
||||
pDesc->SharedSystemMemory = desc.SharedSystemMemory;
|
||||
pDesc->AdapterLuid = desc.AdapterLuid;
|
||||
pDesc->Flags = desc.Flags;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc2(DXGI_ADAPTER_DESC2* pDesc) {
|
||||
if (pDesc == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
auto deviceProp = m_adapter->deviceProperties();
|
||||
auto memoryProp = m_adapter->memoryProperties();
|
||||
@ -158,20 +181,22 @@ namespace dxvk {
|
||||
#ifndef _WIN64
|
||||
// The value returned by DXGI is a 32-bit value
|
||||
// on 32-bit platforms, so we need to clamp it
|
||||
VkDeviceSize maxMemory = 0xF0000000;
|
||||
VkDeviceSize maxMemory = 0xC0000000;
|
||||
deviceMemory = std::min(deviceMemory, maxMemory);
|
||||
sharedMemory = std::min(sharedMemory, maxMemory);
|
||||
#endif
|
||||
|
||||
pDesc->VendorId = deviceProp.vendorID;
|
||||
pDesc->DeviceId = deviceProp.deviceID;
|
||||
pDesc->SubSysId = 0;
|
||||
pDesc->Revision = 0;
|
||||
pDesc->DedicatedVideoMemory = deviceMemory;
|
||||
pDesc->DedicatedSystemMemory = 0;
|
||||
pDesc->SharedSystemMemory = sharedMemory;
|
||||
pDesc->AdapterLuid = LUID { 0, 0 }; // TODO implement
|
||||
pDesc->Flags = 0;
|
||||
pDesc->VendorId = deviceProp.vendorID;
|
||||
pDesc->DeviceId = deviceProp.deviceID;
|
||||
pDesc->SubSysId = 0;
|
||||
pDesc->Revision = 0;
|
||||
pDesc->DedicatedVideoMemory = deviceMemory;
|
||||
pDesc->DedicatedSystemMemory = 0;
|
||||
pDesc->SharedSystemMemory = sharedMemory;
|
||||
pDesc->AdapterLuid = LUID { 0, 0 }; // TODO implement
|
||||
pDesc->Flags = 0;
|
||||
pDesc->GraphicsPreemptionGranularity = DXGI_GRAPHICS_PREEMPTION_DMA_BUFFER_BOUNDARY;
|
||||
pDesc->ComputePreemptionGranularity = DXGI_COMPUTE_PREEMPTION_DMA_BUFFER_BOUNDARY;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,9 @@ namespace dxvk {
|
||||
HRESULT STDMETHODCALLTYPE GetDesc1(
|
||||
DXGI_ADAPTER_DESC1* pDesc) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc2(
|
||||
DXGI_ADAPTER_DESC2* pDesc) final;
|
||||
|
||||
Rc<DxvkAdapter> STDMETHODCALLTYPE GetDXVKAdapter() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateDevice(
|
||||
|
@ -125,7 +125,7 @@ namespace dxvk {
|
||||
IDXGIResource* const* ppResources,
|
||||
DXGI_OFFER_RESOURCE_PRIORITY Priority) {
|
||||
|
||||
Logger::err("DxgiDevice::OfferResources: not implemented");
|
||||
Logger::err("DxgiDevice::OfferResources: Not implemented");
|
||||
return DXGI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
@ -134,13 +134,13 @@ namespace dxvk {
|
||||
UINT NumResources,
|
||||
IDXGIResource* const* ppResources,
|
||||
BOOL* pDiscarded) {
|
||||
Logger::err("DxgiDevice::ReclaimResources: not implemented");
|
||||
Logger::err("DxgiDevice::ReclaimResources: Not implemented");
|
||||
return DXGI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiDevice::EnqueueSetEvent(HANDLE hEvent) {
|
||||
Logger::err("DxgiDevice::EnqueueSetEvent: not implemented");
|
||||
Logger::err("DxgiDevice::EnqueueSetEvent: Not implemented");
|
||||
return DXGI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,8 @@ namespace dxvk {
|
||||
if (riid == __uuidof(IUnknown)
|
||||
|| riid == __uuidof(IDXGIObject)
|
||||
|| riid == __uuidof(IDXGIFactory)
|
||||
|| riid == __uuidof(IDXGIFactory1)) {
|
||||
|| riid == __uuidof(IDXGIFactory1)
|
||||
|| riid == __uuidof(IDXGIFactory2)) {
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
@ -41,6 +42,12 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
BOOL STDMETHODCALLTYPE DxgiFactory::IsWindowedStereoEnabled() {
|
||||
// We don't support Stereo 3D at the moment
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSoftwareAdapter(
|
||||
HMODULE Module,
|
||||
IDXGIAdapter** ppAdapter) {
|
||||
@ -58,16 +65,67 @@ namespace dxvk {
|
||||
IUnknown* pDevice,
|
||||
DXGI_SWAP_CHAIN_DESC* pDesc,
|
||||
IDXGISwapChain** ppSwapChain) {
|
||||
if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 desc;
|
||||
desc.Width = pDesc->BufferDesc.Width;
|
||||
desc.Height = pDesc->BufferDesc.Height;
|
||||
desc.Format = pDesc->BufferDesc.Format;
|
||||
desc.Stereo = FALSE;
|
||||
desc.SampleDesc = pDesc->SampleDesc;
|
||||
desc.BufferUsage = pDesc->BufferUsage;
|
||||
desc.BufferCount = pDesc->BufferCount;
|
||||
desc.Scaling = DXGI_SCALING_STRETCH;
|
||||
desc.SwapEffect = pDesc->SwapEffect;
|
||||
desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
|
||||
desc.Flags = pDesc->Flags;
|
||||
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC descFs;
|
||||
descFs.RefreshRate = pDesc->BufferDesc.RefreshRate;
|
||||
descFs.ScanlineOrdering = pDesc->BufferDesc.ScanlineOrdering;
|
||||
descFs.Scaling = pDesc->BufferDesc.Scaling;
|
||||
descFs.Windowed = pDesc->Windowed;
|
||||
|
||||
IDXGISwapChain1* swapChain = nullptr;
|
||||
HRESULT hr = CreateSwapChainForHwnd(
|
||||
pDevice, pDesc->OutputWindow,
|
||||
&desc, &descFs, nullptr,
|
||||
&swapChain);
|
||||
|
||||
*ppSwapChain = swapChain;
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForHwnd(
|
||||
IUnknown* pDevice,
|
||||
HWND hWnd,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) {
|
||||
InitReturnPtr(ppSwapChain);
|
||||
|
||||
if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == NULL)
|
||||
if (ppSwapChain == nullptr || pDesc == nullptr || hWnd == nullptr || pDevice == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
if (pDesc->OutputWindow == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
// If necessary, set up a default set of
|
||||
// fullscreen parameters for the swap chain
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreenDesc;
|
||||
|
||||
if (pFullscreenDesc != nullptr) {
|
||||
fullscreenDesc = *pFullscreenDesc;
|
||||
} else {
|
||||
fullscreenDesc.RefreshRate = { 0, 0 };
|
||||
fullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
||||
fullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
||||
fullscreenDesc.Windowed = TRUE;
|
||||
}
|
||||
|
||||
try {
|
||||
*ppSwapChain = ref(new DxgiSwapChain(this, pDevice, pDesc));
|
||||
*ppSwapChain = ref(new DxgiSwapChain(this,
|
||||
pDevice, hWnd, pDesc, &fullscreenDesc));
|
||||
return S_OK;
|
||||
} catch (const DxvkError& e) {
|
||||
Logger::err(e.message());
|
||||
@ -76,6 +134,31 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForCoreWindow(
|
||||
IUnknown* pDevice,
|
||||
IUnknown* pWindow,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) {
|
||||
InitReturnPtr(ppSwapChain);
|
||||
|
||||
Logger::err("DxgiFactory::CreateSwapChainForCoreWindow: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForComposition(
|
||||
IUnknown* pDevice,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) {
|
||||
InitReturnPtr(ppSwapChain);
|
||||
|
||||
Logger::err("DxgiFactory::CreateSwapChainForComposition: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapters(
|
||||
UINT Adapter,
|
||||
IDXGIAdapter** ppAdapter) {
|
||||
@ -117,6 +200,14 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::GetSharedResourceAdapterLuid(
|
||||
HANDLE hResource,
|
||||
LUID* pLuid) {
|
||||
Logger::err("DxgiFactory::GetSharedResourceAdapterLuid: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::MakeWindowAssociation(HWND WindowHandle, UINT Flags) {
|
||||
Logger::warn("DXGI: MakeWindowAssociation: Ignoring flags");
|
||||
m_associatedWindow = WindowHandle;
|
||||
@ -129,4 +220,50 @@ namespace dxvk {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusWindow(
|
||||
HWND WindowHandle,
|
||||
UINT wMsg,
|
||||
DWORD* pdwCookie) {
|
||||
Logger::err("DxgiFactory::RegisterOcclusionStatusWindow: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusEvent(
|
||||
HANDLE hEvent,
|
||||
DWORD* pdwCookie) {
|
||||
Logger::err("DxgiFactory::RegisterStereoStatusEvent: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusWindow(
|
||||
HWND WindowHandle,
|
||||
UINT wMsg,
|
||||
DWORD* pdwCookie) {
|
||||
Logger::err("DxgiFactory::RegisterStereoStatusWindow: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusEvent(
|
||||
HANDLE hEvent,
|
||||
DWORD* pdwCookie) {
|
||||
Logger::err("DxgiFactory::RegisterOcclusionStatusEvent: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
void STDMETHODCALLTYPE DxgiFactory::UnregisterStereoStatus(
|
||||
DWORD dwCookie) {
|
||||
Logger::err("DxgiFactory::UnregisterStereoStatus: Not implemented");
|
||||
}
|
||||
|
||||
|
||||
void STDMETHODCALLTYPE DxgiFactory::UnregisterOcclusionStatus(
|
||||
DWORD dwCookie) {
|
||||
Logger::err("DxgiFactory::UnregisterOcclusionStatus: Not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class DxgiFactory : public DxgiObject<IDXGIFactory1> {
|
||||
class DxgiFactory : public DxgiObject<IDXGIFactory2> {
|
||||
|
||||
public:
|
||||
|
||||
@ -23,6 +23,8 @@ namespace dxvk {
|
||||
REFIID riid,
|
||||
void** ppParent) final;
|
||||
|
||||
BOOL STDMETHODCALLTYPE IsWindowedStereoEnabled() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
|
||||
HMODULE Module,
|
||||
IDXGIAdapter** ppAdapter) final;
|
||||
@ -32,6 +34,27 @@ namespace dxvk {
|
||||
DXGI_SWAP_CHAIN_DESC* pDesc,
|
||||
IDXGISwapChain** ppSwapChain) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateSwapChainForHwnd(
|
||||
IUnknown* pDevice,
|
||||
HWND hWnd,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateSwapChainForCoreWindow(
|
||||
IUnknown* pDevice,
|
||||
IUnknown* pWindow,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateSwapChainForComposition(
|
||||
IUnknown* pDevice,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
IDXGIOutput* pRestrictToOutput,
|
||||
IDXGISwapChain1** ppSwapChain) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EnumAdapters(
|
||||
UINT Adapter,
|
||||
IDXGIAdapter** ppAdapter) final;
|
||||
@ -43,11 +66,39 @@ namespace dxvk {
|
||||
HRESULT STDMETHODCALLTYPE GetWindowAssociation(
|
||||
HWND* pWindowHandle) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSharedResourceAdapterLuid(
|
||||
HANDLE hResource,
|
||||
LUID* pLuid) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
|
||||
HWND WindowHandle,
|
||||
UINT Flags) final;
|
||||
|
||||
BOOL STDMETHODCALLTYPE IsCurrent();
|
||||
BOOL STDMETHODCALLTYPE IsCurrent() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE RegisterOcclusionStatusWindow(
|
||||
HWND WindowHandle,
|
||||
UINT wMsg,
|
||||
DWORD* pdwCookie) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE RegisterStereoStatusEvent(
|
||||
HANDLE hEvent,
|
||||
DWORD* pdwCookie) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE RegisterStereoStatusWindow(
|
||||
HWND WindowHandle,
|
||||
UINT wMsg,
|
||||
DWORD* pdwCookie) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE RegisterOcclusionStatusEvent(
|
||||
HANDLE hEvent,
|
||||
DWORD* pdwCookie) final;
|
||||
|
||||
void STDMETHODCALLTYPE UnregisterStereoStatus(
|
||||
DWORD dwCookie) final;
|
||||
|
||||
void STDMETHODCALLTYPE UnregisterOcclusionStatus(
|
||||
DWORD dwCookie) final;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -40,7 +40,7 @@ IDXGIVkDevice : public IDXGIDevice2 {
|
||||
* this interface.
|
||||
*/
|
||||
MIDL_INTERFACE("907bf281-ea3c-43b4-a8e4-9f231107b4ff")
|
||||
IDXGIVkAdapter : public IDXGIAdapter1 {
|
||||
IDXGIVkAdapter : public IDXGIAdapter2 {
|
||||
static const GUID guid;
|
||||
|
||||
virtual dxvk::Rc<dxvk::DxvkAdapter> STDMETHODCALLTYPE GetDXVKAdapter() = 0;
|
||||
@ -109,7 +109,7 @@ IDXGIVkPresenter : public IUnknown {
|
||||
* \returns \c S_OK on success
|
||||
*/
|
||||
virtual HRESULT STDMETHODCALLTYPE CreateSwapChainBackBuffer(
|
||||
const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pSwapChainDesc,
|
||||
IDXGIVkBackBuffer** ppBackBuffer) = 0;
|
||||
|
||||
/**
|
||||
|
@ -7,7 +7,8 @@ namespace dxvk {
|
||||
|
||||
HRESULT createDxgiFactory(REFIID riid, void **ppFactory) {
|
||||
if (riid != __uuidof(IDXGIFactory)
|
||||
&& riid != __uuidof(IDXGIFactory1)) {
|
||||
&& riid != __uuidof(IDXGIFactory1)
|
||||
&& riid != __uuidof(IDXGIFactory2)) {
|
||||
Logger::err("CreateDXGIFactory: Requested version of IDXGIFactory not supported");
|
||||
Logger::err(str::format(riid));
|
||||
*ppFactory = nullptr;
|
||||
|
20
src/dxgi/dxgi_options.cpp
Normal file
20
src/dxgi/dxgi_options.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "dxgi_options.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
const static std::unordered_map<std::string, DxgiOptions> g_dxgiAppOptions = {{
|
||||
{ "Frostpunk.exe", DxgiOptions(DxgiOption::DeferSurfaceCreation) },
|
||||
}};
|
||||
|
||||
|
||||
DxgiOptions getDxgiAppOptions(const std::string& appName) {
|
||||
auto appOptions = g_dxgiAppOptions.find(appName);
|
||||
|
||||
return appOptions != g_dxgiAppOptions.end()
|
||||
? appOptions->second
|
||||
: DxgiOptions();
|
||||
}
|
||||
|
||||
}
|
30
src/dxgi/dxgi_options.h
Normal file
30
src/dxgi/dxgi_options.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "dxgi_include.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief DXGI options
|
||||
*
|
||||
* Per-app options that control the
|
||||
* behaviour of some DXGI classes.
|
||||
*/
|
||||
enum class DxgiOption : uint64_t {
|
||||
/// Defer surface creation until first present call. This
|
||||
/// fixes issues with games that create multiple swap chains
|
||||
/// for a single window that may interfere with each other.
|
||||
DeferSurfaceCreation,
|
||||
};
|
||||
|
||||
using DxgiOptions = Flags<DxgiOption>;
|
||||
|
||||
/**
|
||||
* \brief Gets app-specific DXGI options
|
||||
*
|
||||
* \param [in] appName Application name
|
||||
* \returns DXGI options for this application
|
||||
*/
|
||||
DxgiOptions getDxgiAppOptions(const std::string& appName);
|
||||
|
||||
}
|
@ -10,14 +10,16 @@ namespace dxvk {
|
||||
DxgiVkPresenter::DxgiVkPresenter(
|
||||
const Rc<DxvkDevice>& device,
|
||||
HWND window)
|
||||
: m_device (device),
|
||||
: m_window (window),
|
||||
m_device (device),
|
||||
m_context (device->createContext()) {
|
||||
|
||||
// Create Vulkan surface for the window
|
||||
HINSTANCE instance = reinterpret_cast<HINSTANCE>(
|
||||
GetWindowLongPtr(window, GWLP_HINSTANCE));
|
||||
// Some games don't work with deferred surface creation,
|
||||
// so we should default to initializing it immediately.
|
||||
DxgiOptions dxgiOptions = getDxgiAppOptions(env::getExeName());
|
||||
|
||||
m_surface = m_device->adapter()->createSurface(instance, window);
|
||||
if (!dxgiOptions.test(DxgiOption::DeferSurfaceCreation))
|
||||
m_surface = CreateSurface();
|
||||
|
||||
// Reset options for the swap chain itself. We will
|
||||
// create a swap chain object before presentation.
|
||||
@ -275,27 +277,52 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void DxgiVkPresenter::RecreateSwapchain(const DxvkSwapchainProperties* pOptions) {
|
||||
void DxgiVkPresenter::SetGammaControl(
|
||||
const DXGI_VK_GAMMA_CURVE* pGammaCurve) {
|
||||
m_context->beginRecording(
|
||||
m_device->createCommandList());
|
||||
|
||||
m_context->updateImage(m_gammaTexture,
|
||||
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||||
VkOffset3D { 0, 0, 0 },
|
||||
VkExtent3D { DXGI_VK_GAMMA_CP_COUNT, 1, 1 },
|
||||
pGammaCurve, 0, 0);
|
||||
|
||||
m_device->submitCommandList(
|
||||
m_context->endRecording(),
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void DxgiVkPresenter::RecreateSwapchain(DXGI_FORMAT Format, VkPresentModeKHR PresentMode, VkExtent2D WindowSize) {
|
||||
if (m_surface == nullptr)
|
||||
m_surface = CreateSurface();
|
||||
|
||||
DxvkSwapchainProperties options;
|
||||
options.preferredSurfaceFormat = PickSurfaceFormat(Format);
|
||||
options.preferredPresentMode = PickPresentMode(PresentMode);
|
||||
options.preferredBufferSize = WindowSize;
|
||||
|
||||
const bool doRecreate =
|
||||
pOptions->preferredSurfaceFormat.format != m_options.preferredSurfaceFormat.format
|
||||
|| pOptions->preferredSurfaceFormat.colorSpace != m_options.preferredSurfaceFormat.colorSpace
|
||||
|| pOptions->preferredPresentMode != m_options.preferredPresentMode
|
||||
|| pOptions->preferredBufferSize.width != m_options.preferredBufferSize.width
|
||||
|| pOptions->preferredBufferSize.height != m_options.preferredBufferSize.height;
|
||||
options.preferredSurfaceFormat.format != m_options.preferredSurfaceFormat.format
|
||||
|| options.preferredSurfaceFormat.colorSpace != m_options.preferredSurfaceFormat.colorSpace
|
||||
|| options.preferredPresentMode != m_options.preferredPresentMode
|
||||
|| options.preferredBufferSize.width != m_options.preferredBufferSize.width
|
||||
|| options.preferredBufferSize.height != m_options.preferredBufferSize.height;
|
||||
|
||||
if (doRecreate) {
|
||||
Logger::info(str::format(
|
||||
"DxgiVkPresenter: Recreating swap chain: ",
|
||||
"\n Format: ", pOptions->preferredSurfaceFormat.format,
|
||||
"\n Present mode: ", pOptions->preferredPresentMode,
|
||||
"\n Buffer size: ", pOptions->preferredBufferSize.width, "x", pOptions->preferredBufferSize.height));
|
||||
"\n Format: ", options.preferredSurfaceFormat.format,
|
||||
"\n Present mode: ", options.preferredPresentMode,
|
||||
"\n Buffer size: ", options.preferredBufferSize.width, "x", options.preferredBufferSize.height));
|
||||
|
||||
if (m_swapchain == nullptr)
|
||||
m_swapchain = m_device->createSwapchain(m_surface, *pOptions);
|
||||
m_swapchain = m_device->createSwapchain(m_surface, options);
|
||||
else
|
||||
m_swapchain->changeProperties(*pOptions);
|
||||
m_swapchain->changeProperties(options);
|
||||
|
||||
m_options = *pOptions;
|
||||
m_options = options;
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,20 +366,11 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void DxgiVkPresenter::SetGammaControl(
|
||||
const DXGI_VK_GAMMA_CURVE* pGammaCurve) {
|
||||
m_context->beginRecording(
|
||||
m_device->createCommandList());
|
||||
Rc<DxvkSurface> DxgiVkPresenter::CreateSurface() {
|
||||
HINSTANCE instance = reinterpret_cast<HINSTANCE>(
|
||||
GetWindowLongPtr(m_window, GWLP_HINSTANCE));
|
||||
|
||||
m_context->updateImage(m_gammaTexture,
|
||||
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||||
VkOffset3D { 0, 0, 0 },
|
||||
VkExtent3D { DXGI_VK_GAMMA_CP_COUNT, 1, 1 },
|
||||
pGammaCurve, 0, 0);
|
||||
|
||||
m_device->submitCommandList(
|
||||
m_context->endRecording(),
|
||||
nullptr, nullptr);
|
||||
return m_device->adapter()->createSurface(instance, m_window);
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "../spirv/spirv_module.h"
|
||||
|
||||
#include "dxgi_include.h"
|
||||
#include "dxgi_options.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
@ -100,28 +100,14 @@ namespace dxvk {
|
||||
* Only actually recreates the swap chain object
|
||||
* if any of the properties have changed. If no
|
||||
* properties have changed, this is a no-op.
|
||||
* \param [in] options New swap chain options
|
||||
* \param [in] Format New surface format
|
||||
* \param [in] PresentMode Present mode
|
||||
* \param [in] WindowSize Window size
|
||||
*/
|
||||
void RecreateSwapchain(
|
||||
const DxvkSwapchainProperties* pOptions);
|
||||
|
||||
/**
|
||||
* \brief Picks a surface format based on a DXGI format
|
||||
*
|
||||
* This will return a supported format that, if possible,
|
||||
* has properties similar to those of the DXGI format.
|
||||
* \param [in] fmt The DXGI format
|
||||
* \returns The Vulkan format
|
||||
*/
|
||||
VkSurfaceFormatKHR PickSurfaceFormat(DXGI_FORMAT Fmt) const;
|
||||
|
||||
/**
|
||||
* \brief Picks a supported present mode
|
||||
*
|
||||
* \param [in] preferred Preferred present mode
|
||||
* \returns An actually supported present mode
|
||||
*/
|
||||
VkPresentModeKHR PickPresentMode(VkPresentModeKHR Preferred) const;
|
||||
DXGI_FORMAT Format,
|
||||
VkPresentModeKHR PresentMode,
|
||||
VkExtent2D WindowSize);
|
||||
|
||||
/**
|
||||
* \brief Sets gamma curve
|
||||
@ -142,6 +128,8 @@ namespace dxvk {
|
||||
GammaTex = 3,
|
||||
};
|
||||
|
||||
HWND m_window;
|
||||
|
||||
Rc<DxvkDevice> m_device;
|
||||
Rc<DxvkContext> m_context;
|
||||
|
||||
@ -164,6 +152,12 @@ namespace dxvk {
|
||||
DxvkBlendMode m_blendMode;
|
||||
DxvkSwapchainProperties m_options;
|
||||
|
||||
VkSurfaceFormatKHR PickSurfaceFormat(DXGI_FORMAT Fmt) const;
|
||||
|
||||
VkPresentModeKHR PickPresentMode(VkPresentModeKHR Preferred) const;
|
||||
|
||||
Rc<DxvkSurface> CreateSurface();
|
||||
|
||||
Rc<DxvkSampler> CreateSampler(
|
||||
VkFilter Filter,
|
||||
VkSamplerAddressMode AddressMode);
|
||||
|
@ -6,13 +6,16 @@
|
||||
namespace dxvk {
|
||||
|
||||
DxgiSwapChain::DxgiSwapChain(
|
||||
DxgiFactory* factory,
|
||||
IUnknown* pDevice,
|
||||
DXGI_SWAP_CHAIN_DESC* pDesc)
|
||||
: m_factory (factory),
|
||||
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),
|
||||
m_desc (*pDesc),
|
||||
m_descFs (*pFullscreenDesc),
|
||||
m_monitor (nullptr) {
|
||||
|
||||
// Retrieve a device pointer that allows us to
|
||||
// communicate with the underlying D3D device
|
||||
if (FAILED(pDevice->QueryInterface(__uuidof(IDXGIVkPresenter),
|
||||
@ -44,11 +47,11 @@ namespace dxvk {
|
||||
// shall be set to the current window size.
|
||||
const VkExtent2D windowSize = 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;
|
||||
if (m_desc.Width == 0) m_desc.Width = windowSize.width;
|
||||
if (m_desc.Height == 0) m_desc.Height = windowSize.height;
|
||||
|
||||
// Set initial window mode and fullscreen state
|
||||
if (!pDesc->Windowed && FAILED(EnterFullscreenMode(nullptr)))
|
||||
if (!m_descFs.Windowed && FAILED(EnterFullscreenMode(nullptr)))
|
||||
throw DxvkError("DXGI: DxgiSwapChain: Failed to set initial fullscreen state");
|
||||
|
||||
if (FAILED(CreatePresenter()) || FAILED(CreateBackBuffer()))
|
||||
@ -73,7 +76,8 @@ namespace dxvk {
|
||||
if (riid == __uuidof(IUnknown)
|
||||
|| riid == __uuidof(IDXGIObject)
|
||||
|| riid == __uuidof(IDXGIDeviceSubObject)
|
||||
|| riid == __uuidof(IDXGISwapChain)) {
|
||||
|| riid == __uuidof(IDXGISwapChain)
|
||||
|| riid == __uuidof(IDXGISwapChain1)) {
|
||||
*ppvObject = ref(this);
|
||||
return S_OK;
|
||||
}
|
||||
@ -99,7 +103,7 @@ namespace dxvk {
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
if (Buffer > 0) {
|
||||
@ -116,11 +120,11 @@ namespace dxvk {
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
RECT windowRect = { 0, 0, 0, 0 };
|
||||
::GetWindowRect(m_desc.OutputWindow, &windowRect);
|
||||
::GetWindowRect(m_window, &windowRect);
|
||||
|
||||
HMONITOR monitor = ::MonitorFromPoint(
|
||||
{ (windowRect.left + windowRect.right) / 2,
|
||||
@ -134,6 +138,29 @@ namespace dxvk {
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (pDesc == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
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;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (pDesc == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
@ -142,6 +169,29 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
@ -158,18 +208,18 @@ namespace dxvk {
|
||||
IDXGIOutput** ppTarget) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (pFullscreen != nullptr)
|
||||
*pFullscreen = !m_desc.Windowed;
|
||||
*pFullscreen = !m_descFs.Windowed;
|
||||
|
||||
if (ppTarget != nullptr) {
|
||||
*ppTarget = nullptr;
|
||||
|
||||
if (!m_desc.Windowed)
|
||||
if (!m_descFs.Windowed)
|
||||
hr = m_adapter->GetOutputFromMonitor(m_monitor, ppTarget);
|
||||
}
|
||||
|
||||
@ -177,6 +227,38 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc(
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (pDesc == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
*pDesc = m_descFs;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetHwnd(
|
||||
HWND* pHwnd) {
|
||||
if (pHwnd == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
*pHwnd = m_window;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetCoreWindow(
|
||||
REFIID refiid,
|
||||
void** ppUnk) {
|
||||
InitReturnPtr(ppUnk);
|
||||
|
||||
Logger::err("DxgiSwapChain::GetCoreWindow: Not implemented");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
@ -188,10 +270,17 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
BOOL STDMETHODCALLTYPE DxgiSwapChain::IsTemporaryMonoSupported() {
|
||||
// This seems to be related to stereo 3D display
|
||||
// modes, which we don't support at the moment
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
if (Flags & DXGI_PRESENT_TEST)
|
||||
@ -217,15 +306,11 @@ namespace dxvk {
|
||||
// up vertical synchronization properly, but also apply
|
||||
// changes that were made to the window size even if the
|
||||
// Vulkan swap chain itself remains valid.
|
||||
DxvkSwapchainProperties swapchainProps;
|
||||
swapchainProps.preferredSurfaceFormat
|
||||
= m_presenter->PickSurfaceFormat(m_desc.BufferDesc.Format);
|
||||
swapchainProps.preferredPresentMode = SyncInterval == 0
|
||||
? m_presenter->PickPresentMode(VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||
: m_presenter->PickPresentMode(VK_PRESENT_MODE_FIFO_KHR);
|
||||
swapchainProps.preferredBufferSize = GetWindowSize();
|
||||
|
||||
m_presenter->RecreateSwapchain(&swapchainProps);
|
||||
VkPresentModeKHR presentMode = SyncInterval == 0
|
||||
? VK_PRESENT_MODE_IMMEDIATE_KHR
|
||||
: VK_PRESENT_MODE_FIFO_KHR;
|
||||
|
||||
m_presenter->RecreateSwapchain(m_desc.Format, presentMode, GetWindowSize());
|
||||
m_presenter->PresentImage();
|
||||
return S_OK;
|
||||
} catch (const DxvkError& err) {
|
||||
@ -235,6 +320,17 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers(
|
||||
UINT BufferCount,
|
||||
UINT Width,
|
||||
@ -243,19 +339,19 @@ namespace dxvk {
|
||||
UINT SwapChainFlags) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
const VkExtent2D windowSize = GetWindowSize();
|
||||
|
||||
m_desc.BufferDesc.Width = Width != 0 ? Width : windowSize.width;
|
||||
m_desc.BufferDesc.Height = Height != 0 ? Height : windowSize.height;
|
||||
m_desc.Width = Width != 0 ? Width : windowSize.width;
|
||||
m_desc.Height = Height != 0 ? Height : windowSize.height;
|
||||
|
||||
if (BufferCount != 0)
|
||||
m_desc.BufferCount = BufferCount;
|
||||
|
||||
if (NewFormat != DXGI_FORMAT_UNKNOWN)
|
||||
m_desc.BufferDesc.Format = NewFormat;
|
||||
m_desc.Format = NewFormat;
|
||||
|
||||
return CreateBackBuffer();
|
||||
}
|
||||
@ -267,29 +363,29 @@ namespace dxvk {
|
||||
if (pNewTargetParameters == nullptr)
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
// Update the swap chain description
|
||||
if (pNewTargetParameters->RefreshRate.Numerator != 0)
|
||||
m_desc.BufferDesc.RefreshRate = pNewTargetParameters->RefreshRate;
|
||||
m_descFs.RefreshRate = pNewTargetParameters->RefreshRate;
|
||||
|
||||
m_desc.BufferDesc.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering;
|
||||
m_desc.BufferDesc.Scaling = pNewTargetParameters->Scaling;
|
||||
m_descFs.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering;
|
||||
m_descFs.Scaling = pNewTargetParameters->Scaling;
|
||||
|
||||
if (m_desc.Windowed) {
|
||||
if (m_descFs.Windowed) {
|
||||
// Adjust window position and size
|
||||
RECT newRect = { 0, 0, 0, 0 };
|
||||
RECT oldRect = { 0, 0, 0, 0 };
|
||||
|
||||
::GetWindowRect(m_desc.OutputWindow, &oldRect);
|
||||
::GetWindowRect(m_window, &oldRect);
|
||||
::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
|
||||
::AdjustWindowRectEx(&newRect,
|
||||
::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE,
|
||||
::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE));
|
||||
::GetWindowLongW(m_window, GWL_STYLE), FALSE,
|
||||
::GetWindowLongW(m_window, GWL_EXSTYLE));
|
||||
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
|
||||
::OffsetRect(&newRect, oldRect.left, oldRect.top);
|
||||
::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
|
||||
::MoveWindow(m_window, newRect.left, newRect.top,
|
||||
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
|
||||
} else {
|
||||
Com<IDXGIOutput> output;
|
||||
@ -309,7 +405,7 @@ namespace dxvk {
|
||||
|
||||
const RECT newRect = desc.DesktopCoordinates;
|
||||
|
||||
::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
|
||||
::MoveWindow(m_window, newRect.left, newRect.top,
|
||||
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
|
||||
}
|
||||
|
||||
@ -323,18 +419,32 @@ namespace dxvk {
|
||||
IDXGIOutput* pTarget) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (!IsWindow(m_desc.OutputWindow))
|
||||
if (!IsWindow(m_window))
|
||||
return DXGI_ERROR_INVALID_CALL;
|
||||
|
||||
if (m_desc.Windowed && Fullscreen)
|
||||
if (m_descFs.Windowed && Fullscreen)
|
||||
return this->EnterFullscreenMode(pTarget);
|
||||
else if (!m_desc.Windowed && !Fullscreen)
|
||||
else if (!m_descFs.Windowed && !Fullscreen)
|
||||
return this->LeaveFullscreenMode();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
HRESULT DxgiSwapChain::SetGammaControl(const DXGI_GAMMA_CONTROL* pGammaControl) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
@ -373,7 +483,7 @@ namespace dxvk {
|
||||
try {
|
||||
m_presenter = new DxgiVkPresenter(
|
||||
m_device->GetDXVKDevice(),
|
||||
m_desc.OutputWindow);
|
||||
m_window);
|
||||
return S_OK;
|
||||
} catch (const DxvkError& e) {
|
||||
Logger::err(e.message());
|
||||
@ -412,7 +522,7 @@ namespace dxvk {
|
||||
VkExtent2D DxgiSwapChain::GetWindowSize() const {
|
||||
RECT windowRect;
|
||||
|
||||
if (!::GetClientRect(m_desc.OutputWindow, &windowRect))
|
||||
if (!::GetClientRect(m_window, &windowRect))
|
||||
windowRect = RECT();
|
||||
|
||||
VkExtent2D result;
|
||||
@ -433,14 +543,18 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
// Find a display mode that matches what we need
|
||||
::GetWindowRect(m_desc.OutputWindow, &m_windowState.rect);
|
||||
::GetWindowRect(m_window, &m_windowState.rect);
|
||||
|
||||
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) {
|
||||
auto windowRect = m_windowState.rect;
|
||||
|
||||
DXGI_MODE_DESC displayMode = m_desc.BufferDesc;
|
||||
displayMode.Width = windowRect.right - windowRect.left;
|
||||
displayMode.Height = windowRect.bottom - windowRect.top;
|
||||
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;
|
||||
|
||||
if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) {
|
||||
Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode");
|
||||
@ -449,11 +563,11 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
// Update swap chain description
|
||||
m_desc.Windowed = FALSE;
|
||||
m_descFs.Windowed = FALSE;
|
||||
|
||||
// Change the window flags to remove the decoration etc.
|
||||
LONG style = ::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE);
|
||||
LONG exstyle = ::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE);
|
||||
LONG style = ::GetWindowLongW(m_window, GWL_STYLE);
|
||||
LONG exstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE);
|
||||
|
||||
m_windowState.style = style;
|
||||
m_windowState.exstyle = exstyle;
|
||||
@ -461,8 +575,8 @@ namespace dxvk {
|
||||
style &= ~WS_OVERLAPPEDWINDOW;
|
||||
exstyle &= ~WS_EX_OVERLAPPEDWINDOW;
|
||||
|
||||
::SetWindowLongW(m_desc.OutputWindow, GWL_STYLE, style);
|
||||
::SetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE, exstyle);
|
||||
::SetWindowLongW(m_window, GWL_STYLE, style);
|
||||
::SetWindowLongW(m_window, GWL_EXSTYLE, exstyle);
|
||||
|
||||
// Move the window so that it covers the entire output
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
@ -470,7 +584,7 @@ namespace dxvk {
|
||||
|
||||
const RECT rect = desc.DesktopCoordinates;
|
||||
|
||||
::SetWindowPos(m_desc.OutputWindow, HWND_TOPMOST,
|
||||
::SetWindowPos(m_window, HWND_TOPMOST,
|
||||
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
||||
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
|
||||
|
||||
@ -487,24 +601,24 @@ namespace dxvk {
|
||||
Logger::warn("DXGI: LeaveFullscreenMode: Failed to restore display mode");
|
||||
|
||||
// Restore internal state
|
||||
m_desc.Windowed = TRUE;
|
||||
m_descFs.Windowed = TRUE;
|
||||
m_monitor = nullptr;
|
||||
|
||||
// Only restore the window style if the application hasn't
|
||||
// changed them. This is in line with what native DXGI does.
|
||||
LONG curStyle = ::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE) & ~WS_VISIBLE;
|
||||
LONG curExstyle = ::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
|
||||
LONG curStyle = ::GetWindowLongW(m_window, GWL_STYLE) & ~WS_VISIBLE;
|
||||
LONG curExstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
|
||||
|
||||
if (curStyle == (m_windowState.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
|
||||
&& curExstyle == (m_windowState.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
|
||||
::SetWindowLongW(m_desc.OutputWindow, GWL_STYLE, m_windowState.style);
|
||||
::SetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE, m_windowState.exstyle);
|
||||
::SetWindowLongW(m_window, GWL_STYLE, m_windowState.style);
|
||||
::SetWindowLongW(m_window, GWL_EXSTYLE, m_windowState.exstyle);
|
||||
}
|
||||
|
||||
// Restore window position and apply the style
|
||||
const RECT rect = m_windowState.rect;
|
||||
|
||||
::SetWindowPos(m_desc.OutputWindow, 0,
|
||||
::SetWindowPos(m_window, 0,
|
||||
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
||||
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
|
@ -20,69 +20,107 @@ namespace dxvk {
|
||||
class DxgiFactory;
|
||||
class DxgiOutput;
|
||||
|
||||
class DxgiSwapChain : public DxgiObject<IDXGISwapChain> {
|
||||
class DxgiSwapChain : public DxgiObject<IDXGISwapChain1> {
|
||||
|
||||
public:
|
||||
|
||||
DxgiSwapChain(
|
||||
DxgiFactory* factory,
|
||||
IUnknown* pDevice,
|
||||
DXGI_SWAP_CHAIN_DESC* pDesc);
|
||||
DxgiFactory* pFactory,
|
||||
IUnknown* pDevice,
|
||||
HWND hWnd,
|
||||
const DXGI_SWAP_CHAIN_DESC1* pDesc,
|
||||
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc);
|
||||
|
||||
~DxgiSwapChain();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void** ppvObject) final;
|
||||
REFIID riid,
|
||||
void** ppvObject) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetParent(
|
||||
REFIID riid,
|
||||
void** ppParent) final;
|
||||
REFIID riid,
|
||||
void** ppParent) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDevice(
|
||||
REFIID riid,
|
||||
void** ppDevice) final;
|
||||
REFIID riid,
|
||||
void** ppDevice) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetBuffer(
|
||||
UINT Buffer,
|
||||
REFIID riid,
|
||||
void** ppSurface) final;
|
||||
UINT Buffer,
|
||||
REFIID riid,
|
||||
void** ppSurface) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetContainingOutput(
|
||||
IDXGIOutput** ppOutput) final;
|
||||
IDXGIOutput** ppOutput) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDesc(
|
||||
DXGI_SWAP_CHAIN_DESC* pDesc) final;
|
||||
DXGI_SWAP_CHAIN_DESC* pDesc) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetFrameStatistics(
|
||||
DXGI_FRAME_STATISTICS* pStats) final;
|
||||
HRESULT STDMETHODCALLTYPE GetDesc1(
|
||||
DXGI_SWAP_CHAIN_DESC1* pDesc) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetFullscreenState(
|
||||
BOOL* pFullscreen,
|
||||
IDXGIOutput** ppTarget) final;
|
||||
BOOL* pFullscreen,
|
||||
IDXGIOutput** ppTarget) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetFullscreenDesc(
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetHwnd(
|
||||
HWND* pHwnd) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetCoreWindow(
|
||||
REFIID refiid,
|
||||
void** ppUnk) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetBackgroundColor(
|
||||
DXGI_RGBA* pColor) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetRotation(
|
||||
DXGI_MODE_ROTATION* pRotation) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetRestrictToOutput(
|
||||
IDXGIOutput** ppRestrictToOutput) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetFrameStatistics(
|
||||
DXGI_FRAME_STATISTICS* pStats) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetLastPresentCount(
|
||||
UINT* pLastPresentCount) final;
|
||||
UINT* pLastPresentCount) final;
|
||||
|
||||
BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Present(
|
||||
UINT SyncInterval,
|
||||
UINT Flags) final;
|
||||
UINT SyncInterval,
|
||||
UINT Flags) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Present1(
|
||||
UINT SyncInterval,
|
||||
UINT PresentFlags,
|
||||
const DXGI_PRESENT_PARAMETERS* pPresentParameters) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ResizeBuffers(
|
||||
UINT BufferCount,
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
DXGI_FORMAT NewFormat,
|
||||
UINT SwapChainFlags) final;
|
||||
UINT BufferCount,
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
DXGI_FORMAT NewFormat,
|
||||
UINT SwapChainFlags) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ResizeTarget(
|
||||
const DXGI_MODE_DESC* pNewTargetParameters) final;
|
||||
const DXGI_MODE_DESC* pNewTargetParameters) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetFullscreenState(
|
||||
BOOL Fullscreen,
|
||||
IDXGIOutput* pTarget) final;
|
||||
BOOL Fullscreen,
|
||||
IDXGIOutput* pTarget) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetBackgroundColor(
|
||||
const DXGI_RGBA* pColor) final;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetRotation(
|
||||
DXGI_MODE_ROTATION Rotation) final;
|
||||
|
||||
HRESULT SetGammaControl(
|
||||
const DXGI_GAMMA_CONTROL* pGammaControl);
|
||||
const DXGI_GAMMA_CONTROL* pGammaControl);
|
||||
|
||||
HRESULT SetDefaultGammaControl();
|
||||
|
||||
@ -101,7 +139,9 @@ namespace dxvk {
|
||||
Com<DxgiDevice> m_device;
|
||||
Com<IDXGIVkPresenter> m_presentDevice;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC m_desc;
|
||||
HWND m_window;
|
||||
DXGI_SWAP_CHAIN_DESC1 m_desc;
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_descFs;
|
||||
DXGI_FRAME_STATISTICS m_stats;
|
||||
|
||||
Rc<DxgiVkPresenter> m_presenter;
|
||||
|
@ -10,6 +10,7 @@ dxgi_src = [
|
||||
'dxgi_factory.cpp',
|
||||
'dxgi_format.cpp',
|
||||
'dxgi_main.cpp',
|
||||
'dxgi_options.cpp',
|
||||
'dxgi_output.cpp',
|
||||
'dxgi_presenter.cpp',
|
||||
'dxgi_swapchain.cpp',
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "dxvk_compute.h"
|
||||
#include "dxvk_device.h"
|
||||
#include "dxvk_spec_const.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
@ -99,19 +100,16 @@ namespace dxvk {
|
||||
Logger::debug(str::format(" cs : ", m_cs->shader()->debugName()));
|
||||
}
|
||||
|
||||
std::array<VkBool32, MaxNumActiveBindings> specData;
|
||||
std::array<VkSpecializationMapEntry, MaxNumActiveBindings> specMap;
|
||||
DxvkSpecConstantData specData;
|
||||
|
||||
for (uint32_t i = 0; i < MaxNumActiveBindings; i++) {
|
||||
specData[i] = state.bsBindingState.isBound(i) ? VK_TRUE : VK_FALSE;
|
||||
specMap [i] = { i, static_cast<uint32_t>(sizeof(VkBool32)) * i, sizeof(VkBool32) };
|
||||
}
|
||||
for (uint32_t i = 0; i < MaxNumActiveBindings; i++)
|
||||
specData.activeBindings[i] = state.bsBindingState.isBound(i) ? VK_TRUE : VK_FALSE;
|
||||
|
||||
VkSpecializationInfo specInfo;
|
||||
specInfo.mapEntryCount = specMap.size();
|
||||
specInfo.pMapEntries = specMap.data();
|
||||
specInfo.dataSize = specData.size() * sizeof(VkBool32);
|
||||
specInfo.pData = specData.data();
|
||||
specInfo.mapEntryCount = g_specConstantMap.mapEntryCount();
|
||||
specInfo.pMapEntries = g_specConstantMap.mapEntryData();
|
||||
specInfo.dataSize = sizeof(specData);
|
||||
specInfo.pData = &specData;
|
||||
|
||||
VkComputePipelineCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
|
@ -7,12 +7,14 @@
|
||||
namespace dxvk {
|
||||
|
||||
DxvkContext::DxvkContext(
|
||||
const Rc<DxvkDevice>& device,
|
||||
const Rc<DxvkPipelineManager>& pipelineManager,
|
||||
const Rc<DxvkMetaClearObjects>& metaClearObjects)
|
||||
const Rc<DxvkDevice>& device,
|
||||
const Rc<DxvkPipelineManager>& pipelineManager,
|
||||
const Rc<DxvkMetaClearObjects>& metaClearObjects,
|
||||
const Rc<DxvkMetaMipGenObjects>& metaMipGenObjects)
|
||||
: m_device (device),
|
||||
m_pipeMgr (pipelineManager),
|
||||
m_metaClear (metaClearObjects) { }
|
||||
m_metaClear (metaClearObjects),
|
||||
m_metaMipGen(metaMipGenObjects) { }
|
||||
|
||||
|
||||
DxvkContext::~DxvkContext() {
|
||||
@ -228,6 +230,9 @@ namespace dxvk {
|
||||
uint32_t value) {
|
||||
this->spillRenderPass();
|
||||
|
||||
if (length == buffer->info().size)
|
||||
length = align(length, 4);
|
||||
|
||||
auto slice = buffer->subSlice(offset, length);
|
||||
|
||||
m_cmd->cmdFillBuffer(
|
||||
@ -976,123 +981,102 @@ namespace dxvk {
|
||||
|
||||
|
||||
void DxvkContext::generateMipmaps(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& subresources) {
|
||||
if (subresources.levelCount <= 1)
|
||||
const Rc<DxvkImageView>& imageView) {
|
||||
if (imageView->info().numLevels <= 1)
|
||||
return;
|
||||
|
||||
this->spillRenderPass();
|
||||
|
||||
// The top-most level will only be read. We can
|
||||
// discard the contents of all the lower levels
|
||||
// since we're going to override them anyway.
|
||||
m_barriers.accessImage(image,
|
||||
VkImageSubresourceRange {
|
||||
subresources.aspectMask,
|
||||
subresources.baseMipLevel, 1,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount },
|
||||
image->info().layout,
|
||||
image->info().stages,
|
||||
image->info().access,
|
||||
image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_READ_BIT);
|
||||
this->unbindGraphicsPipeline();
|
||||
|
||||
m_barriers.accessImage(image,
|
||||
VkImageSubresourceRange {
|
||||
subresources.aspectMask,
|
||||
subresources.baseMipLevel + 1,
|
||||
subresources.levelCount - 1,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount },
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
image->info().stages,
|
||||
image->info().access,
|
||||
image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
// Create the a set of framebuffers and image views
|
||||
const Rc<DxvkMetaMipGenRenderPass> mipGenerator
|
||||
= new DxvkMetaMipGenRenderPass(m_device->vkd(), imageView);
|
||||
|
||||
m_barriers.recordCommands(m_cmd);
|
||||
// Common descriptor set properties that we use to
|
||||
// bind the source image view to the fragment shader
|
||||
VkDescriptorImageInfo descriptorImage;
|
||||
descriptorImage.sampler = VK_NULL_HANDLE;
|
||||
descriptorImage.imageView = VK_NULL_HANDLE;
|
||||
descriptorImage.imageLayout = imageView->imageInfo().layout;
|
||||
|
||||
// Generate each individual mip level with a blit
|
||||
for (uint32_t i = 1; i < subresources.levelCount; i++) {
|
||||
const uint32_t mip = subresources.baseMipLevel + i;
|
||||
VkWriteDescriptorSet descriptorWrite;
|
||||
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptorWrite.pNext = nullptr;
|
||||
descriptorWrite.dstSet = VK_NULL_HANDLE;
|
||||
descriptorWrite.dstBinding = 0;
|
||||
descriptorWrite.dstArrayElement = 0;
|
||||
descriptorWrite.descriptorCount = 1;
|
||||
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descriptorWrite.pImageInfo = &descriptorImage;
|
||||
descriptorWrite.pBufferInfo = nullptr;
|
||||
descriptorWrite.pTexelBufferView = nullptr;
|
||||
|
||||
// Common render pass info
|
||||
VkRenderPassBeginInfo passInfo;
|
||||
passInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
passInfo.pNext = nullptr;
|
||||
passInfo.renderPass = mipGenerator->renderPass();
|
||||
passInfo.framebuffer = VK_NULL_HANDLE;
|
||||
passInfo.renderArea = VkRect2D { };
|
||||
passInfo.clearValueCount = 0;
|
||||
passInfo.pClearValues = nullptr;
|
||||
|
||||
// Retrieve a compatible pipeline to use for rendering
|
||||
DxvkMetaMipGenPipeline pipeInfo = m_metaMipGen->getPipeline(
|
||||
mipGenerator->viewType(), imageView->info().format);
|
||||
|
||||
for (uint32_t i = 0; i < mipGenerator->passCount(); i++) {
|
||||
DxvkMetaMipGenPass pass = mipGenerator->pass(i);
|
||||
|
||||
const VkExtent3D srcExtent = image->mipLevelExtent(mip - 1);
|
||||
const VkExtent3D dstExtent = image->mipLevelExtent(mip);
|
||||
// Width, height and layer count for the current pass
|
||||
VkExtent3D passExtent = mipGenerator->passExtent(i);
|
||||
|
||||
VkImageBlit region;
|
||||
region.srcSubresource = VkImageSubresourceLayers {
|
||||
subresources.aspectMask, mip - 1,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount };
|
||||
region.srcOffsets[0] = VkOffset3D { 0, 0, 0 };
|
||||
region.srcOffsets[1].x = srcExtent.width;
|
||||
region.srcOffsets[1].y = srcExtent.height;
|
||||
region.srcOffsets[1].z = srcExtent.depth;
|
||||
// Create descriptor set with the current source view
|
||||
descriptorImage.imageView = pass.srcView;
|
||||
descriptorWrite.dstSet = m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout);
|
||||
m_cmd->updateDescriptorSets(1, &descriptorWrite);
|
||||
|
||||
region.dstSubresource = VkImageSubresourceLayers {
|
||||
subresources.aspectMask, mip,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount };
|
||||
region.dstOffsets[0] = VkOffset3D { 0, 0, 0 };
|
||||
region.dstOffsets[1].x = dstExtent.width;
|
||||
region.dstOffsets[1].y = dstExtent.height;
|
||||
region.dstOffsets[1].z = dstExtent.depth;
|
||||
// Set up viewport and scissor rect
|
||||
VkViewport viewport;
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = float(passExtent.width);
|
||||
viewport.height = float(passExtent.height);
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
m_cmd->cmdBlitImage(
|
||||
image->handle(), image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL),
|
||||
image->handle(), image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL),
|
||||
1, ®ion, VK_FILTER_LINEAR);
|
||||
VkRect2D scissor;
|
||||
scissor.offset = { 0, 0 };
|
||||
scissor.extent = { passExtent.width, passExtent.height };
|
||||
|
||||
if (i + 1 < subresources.levelCount) {
|
||||
m_barriers.accessImage(image,
|
||||
VkImageSubresourceRange {
|
||||
subresources.aspectMask, mip, 1,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount },
|
||||
image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_READ_BIT);
|
||||
m_barriers.recordCommands(m_cmd);
|
||||
}
|
||||
// Set up render pass info
|
||||
passInfo.framebuffer = pass.framebuffer;
|
||||
passInfo.renderArea = scissor;
|
||||
|
||||
// Set up push constants
|
||||
DxvkMetaMipGenPushConstants pushConstants;
|
||||
pushConstants.layerCount = passExtent.depth;
|
||||
|
||||
m_cmd->cmdBeginRenderPass(&passInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeInfo.pipeHandle);
|
||||
m_cmd->cmdBindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipeInfo.pipeLayout, descriptorWrite.dstSet);
|
||||
|
||||
m_cmd->cmdSetViewport(0, 1, &viewport);
|
||||
m_cmd->cmdSetScissor (0, 1, &scissor);
|
||||
|
||||
m_cmd->cmdPushConstants(
|
||||
pipeInfo.pipeLayout,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(pushConstants),
|
||||
&pushConstants);
|
||||
|
||||
m_cmd->cmdDraw(1, passExtent.depth, 0, 0);
|
||||
m_cmd->cmdEndRenderPass();
|
||||
}
|
||||
|
||||
// Transform mip levels back into their original layout.
|
||||
// The last mip level is still in TRANSFER_DST_OPTIMAL.
|
||||
m_barriers.accessImage(image,
|
||||
VkImageSubresourceRange {
|
||||
subresources.aspectMask,
|
||||
subresources.baseMipLevel,
|
||||
subresources.levelCount - 1,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount },
|
||||
image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_READ_BIT,
|
||||
image->info().layout,
|
||||
image->info().stages,
|
||||
image->info().access);
|
||||
|
||||
m_barriers.accessImage(image,
|
||||
VkImageSubresourceRange {
|
||||
subresources.aspectMask,
|
||||
subresources.baseMipLevel
|
||||
+ subresources.levelCount - 1, 1,
|
||||
subresources.baseArrayLayer,
|
||||
subresources.layerCount },
|
||||
image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
image->info().layout,
|
||||
image->info().stages,
|
||||
image->info().access);
|
||||
|
||||
m_barriers.recordCommands(m_cmd);
|
||||
m_cmd->trackResource(mipGenerator);
|
||||
m_cmd->trackResource(imageView->image());
|
||||
}
|
||||
|
||||
|
||||
@ -1399,15 +1383,19 @@ namespace dxvk {
|
||||
}
|
||||
}
|
||||
|
||||
m_cmd->cmdSetViewport(0, viewportCount, m_state.vp.viewports.data());
|
||||
m_cmd->cmdSetScissor (0, viewportCount, m_state.vp.scissorRects.data());
|
||||
if (m_gpActivePipeline != VK_NULL_HANDLE) {
|
||||
m_cmd->cmdSetViewport(0, viewportCount, m_state.vp.viewports.data());
|
||||
m_cmd->cmdSetScissor (0, viewportCount, m_state.vp.scissorRects.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxvkContext::setBlendConstants(
|
||||
const DxvkBlendConstants& blendConstants) {
|
||||
m_state.om.blendConstants = blendConstants;
|
||||
m_cmd->cmdSetBlendConstants(&blendConstants.r);
|
||||
|
||||
if (m_gpActivePipeline != VK_NULL_HANDLE)
|
||||
m_cmd->cmdSetBlendConstants(&blendConstants.r);
|
||||
}
|
||||
|
||||
|
||||
@ -1415,9 +1403,11 @@ namespace dxvk {
|
||||
const uint32_t reference) {
|
||||
m_state.om.stencilReference = reference;
|
||||
|
||||
m_cmd->cmdSetStencilReference(
|
||||
VK_STENCIL_FRONT_AND_BACK,
|
||||
reference);
|
||||
if (m_gpActivePipeline != VK_NULL_HANDLE) {
|
||||
m_cmd->cmdSetStencilReference(
|
||||
VK_STENCIL_FRONT_AND_BACK,
|
||||
reference);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1680,6 +1670,18 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void DxvkContext::unbindGraphicsPipeline() {
|
||||
m_flags.set(
|
||||
DxvkContextFlag::GpDirtyPipeline,
|
||||
DxvkContextFlag::GpDirtyPipelineState,
|
||||
DxvkContextFlag::GpDirtyResources,
|
||||
DxvkContextFlag::GpDirtyVertexBuffers,
|
||||
DxvkContextFlag::GpDirtyIndexBuffer);
|
||||
|
||||
m_gpActivePipeline = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
void DxvkContext::updateGraphicsPipeline() {
|
||||
if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) {
|
||||
m_flags.clr(DxvkContextFlag::GpDirtyPipeline);
|
||||
@ -1822,11 +1824,11 @@ namespace dxvk {
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
if (res.imageView != nullptr && res.imageView->type() == binding.view) {
|
||||
if (res.imageView != nullptr && res.imageView->handle(binding.view) != VK_NULL_HANDLE) {
|
||||
updatePipelineState |= bindingState.setBound(i);
|
||||
|
||||
m_descInfos[i].image.sampler = VK_NULL_HANDLE;
|
||||
m_descInfos[i].image.imageView = res.imageView->handle();
|
||||
m_descInfos[i].image.imageView = res.imageView->handle(binding.view);
|
||||
m_descInfos[i].image.imageLayout = res.imageView->imageInfo().layout;
|
||||
|
||||
if (depthAttachment.view != nullptr
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "dxvk_data.h"
|
||||
#include "dxvk_event.h"
|
||||
#include "dxvk_meta_clear.h"
|
||||
#include "dxvk_meta_mipgen.h"
|
||||
#include "dxvk_meta_resolve.h"
|
||||
#include "dxvk_pipecache.h"
|
||||
#include "dxvk_pipemanager.h"
|
||||
@ -28,9 +29,10 @@ namespace dxvk {
|
||||
public:
|
||||
|
||||
DxvkContext(
|
||||
const Rc<DxvkDevice>& device,
|
||||
const Rc<DxvkPipelineManager>& pipelineManager,
|
||||
const Rc<DxvkMetaClearObjects>& metaClearObjects);
|
||||
const Rc<DxvkDevice>& device,
|
||||
const Rc<DxvkPipelineManager>& pipelineManager,
|
||||
const Rc<DxvkMetaClearObjects>& metaClearObjects,
|
||||
const Rc<DxvkMetaMipGenObjects>& metaMipGenObjects);
|
||||
~DxvkContext();
|
||||
|
||||
/**
|
||||
@ -403,12 +405,10 @@ namespace dxvk {
|
||||
*
|
||||
* Uses blitting to generate lower mip levels from
|
||||
* the top-most mip level passed to this method.
|
||||
* \param [in] image The image to generate mips for
|
||||
* \param [in] subresource The subresource range
|
||||
* \param [in] imageView The image to generate mips for
|
||||
*/
|
||||
void generateMipmaps(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& subresources);
|
||||
const Rc<DxvkImageView>& imageView);
|
||||
|
||||
/**
|
||||
* \brief Initializes or invalidates an image
|
||||
@ -621,6 +621,7 @@ namespace dxvk {
|
||||
const Rc<DxvkDevice> m_device;
|
||||
const Rc<DxvkPipelineManager> m_pipeMgr;
|
||||
const Rc<DxvkMetaClearObjects> m_metaClear;
|
||||
const Rc<DxvkMetaMipGenObjects> m_metaMipGen;
|
||||
|
||||
Rc<DxvkCommandList> m_cmd;
|
||||
DxvkContextFlags m_flags;
|
||||
@ -654,10 +655,10 @@ namespace dxvk {
|
||||
DxvkRenderPassOps& renderPassOps);
|
||||
|
||||
void unbindComputePipeline();
|
||||
|
||||
void updateComputePipeline();
|
||||
void updateComputePipelineState();
|
||||
|
||||
void unbindGraphicsPipeline();
|
||||
void updateGraphicsPipeline();
|
||||
void updateGraphicsPipelineState();
|
||||
|
||||
|
@ -8,17 +8,18 @@ namespace dxvk {
|
||||
const Rc<vk::DeviceFn>& vkd,
|
||||
const Rc<DxvkDeviceExtensions>& extensions,
|
||||
const VkPhysicalDeviceFeatures& features)
|
||||
: m_adapter (adapter),
|
||||
m_vkd (vkd),
|
||||
m_extensions (extensions),
|
||||
m_features (features),
|
||||
m_properties (adapter->deviceProperties()),
|
||||
m_memory (new DxvkMemoryAllocator (adapter, vkd)),
|
||||
m_renderPassPool (new DxvkRenderPassPool (vkd)),
|
||||
m_pipelineManager (new DxvkPipelineManager (this)),
|
||||
m_metaClearObjects(new DxvkMetaClearObjects (vkd)),
|
||||
m_unboundResources(this),
|
||||
m_submissionQueue (this) {
|
||||
: m_adapter (adapter),
|
||||
m_vkd (vkd),
|
||||
m_extensions (extensions),
|
||||
m_features (features),
|
||||
m_properties (adapter->deviceProperties()),
|
||||
m_memory (new DxvkMemoryAllocator (adapter, vkd)),
|
||||
m_renderPassPool (new DxvkRenderPassPool (vkd)),
|
||||
m_pipelineManager (new DxvkPipelineManager (this)),
|
||||
m_metaClearObjects (new DxvkMetaClearObjects (vkd)),
|
||||
m_metaMipGenObjects (new DxvkMetaMipGenObjects(vkd)),
|
||||
m_unboundResources (this),
|
||||
m_submissionQueue (this) {
|
||||
m_graphicsQueue.queueFamily = m_adapter->graphicsQueueFamily();
|
||||
m_presentQueue.queueFamily = m_adapter->presentQueueFamily();
|
||||
|
||||
@ -106,7 +107,8 @@ namespace dxvk {
|
||||
Rc<DxvkContext> DxvkDevice::createContext() {
|
||||
return new DxvkContext(this,
|
||||
m_pipelineManager,
|
||||
m_metaClearObjects);
|
||||
m_metaClearObjects,
|
||||
m_metaMipGenObjects);
|
||||
}
|
||||
|
||||
|
||||
|
@ -356,6 +356,7 @@ namespace dxvk {
|
||||
Rc<DxvkRenderPassPool> m_renderPassPool;
|
||||
Rc<DxvkPipelineManager> m_pipelineManager;
|
||||
Rc<DxvkMetaClearObjects> m_metaClearObjects;
|
||||
Rc<DxvkMetaMipGenObjects> m_metaMipGenObjects;
|
||||
|
||||
DxvkUnboundResources m_unboundResources;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "dxvk_device.h"
|
||||
#include "dxvk_graphics.h"
|
||||
#include "dxvk_spec_const.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
@ -203,19 +204,17 @@ namespace dxvk {
|
||||
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
|
||||
};
|
||||
|
||||
std::array<VkBool32, MaxNumActiveBindings> specData;
|
||||
std::array<VkSpecializationMapEntry, MaxNumActiveBindings> specMap;
|
||||
DxvkSpecConstantData specData;
|
||||
specData.rasterizerSampleCount = uint32_t(state.msSampleCount);
|
||||
|
||||
for (uint32_t i = 0; i < MaxNumActiveBindings; i++) {
|
||||
specData[i] = state.bsBindingState.isBound(i) ? VK_TRUE : VK_FALSE;
|
||||
specMap [i] = { i, static_cast<uint32_t>(sizeof(VkBool32)) * i, sizeof(VkBool32) };
|
||||
}
|
||||
for (uint32_t i = 0; i < MaxNumActiveBindings; i++)
|
||||
specData.activeBindings[i] = state.bsBindingState.isBound(i) ? VK_TRUE : VK_FALSE;
|
||||
|
||||
VkSpecializationInfo specInfo;
|
||||
specInfo.mapEntryCount = specMap.size();
|
||||
specInfo.pMapEntries = specMap.data();
|
||||
specInfo.dataSize = specData.size() * sizeof(VkBool32);
|
||||
specInfo.pData = specData.data();
|
||||
specInfo.mapEntryCount = g_specConstantMap.mapEntryCount();
|
||||
specInfo.pMapEntries = g_specConstantMap.mapEntryData();
|
||||
specInfo.dataSize = sizeof(specData);
|
||||
specInfo.pData = &specData;
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
||||
|
||||
|
@ -4,6 +4,13 @@
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
struct DxvkEq {
|
||||
template<typename T>
|
||||
size_t operator () (const T& a, const T& b) const {
|
||||
return a.eq(b);
|
||||
}
|
||||
};
|
||||
|
||||
struct DxvkHash {
|
||||
template<typename T>
|
||||
size_t operator () (const T& object) const {
|
||||
|
@ -87,25 +87,75 @@ namespace dxvk {
|
||||
const Rc<DxvkImage>& image,
|
||||
const DxvkImageViewCreateInfo& info)
|
||||
: m_vkd(vkd), m_image(image), m_info(info) {
|
||||
// Since applications tend to bind views
|
||||
for (uint32_t i = 0; i < ViewCount; i++)
|
||||
m_views[i] = VK_NULL_HANDLE;
|
||||
|
||||
switch (info.type) {
|
||||
case VK_IMAGE_VIEW_TYPE_1D:
|
||||
case VK_IMAGE_VIEW_TYPE_1D_ARRAY: {
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_1D, 1);
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_1D_ARRAY, info.numLayers);
|
||||
} break;
|
||||
|
||||
case VK_IMAGE_VIEW_TYPE_2D:
|
||||
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
|
||||
case VK_IMAGE_VIEW_TYPE_CUBE:
|
||||
case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: {
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_2D, 1);
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, info.numLayers);
|
||||
|
||||
if (m_image->info().flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) {
|
||||
uint32_t cubeCount = info.numLayers / 6;
|
||||
|
||||
if (cubeCount > 0) {
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_CUBE, 6);
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, 6 * cubeCount);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case VK_IMAGE_VIEW_TYPE_3D: {
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_3D, 1);
|
||||
|
||||
if (m_image->info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR) {
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_2D, 1);
|
||||
this->createView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, m_image->info().extent.depth);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw DxvkError(str::format("DxvkImageView: Invalid view type: ", info.type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxvkImageView::~DxvkImageView() {
|
||||
for (uint32_t i = 0; i < ViewCount; i++)
|
||||
m_vkd->vkDestroyImageView(m_vkd->device(), m_views[i], nullptr);
|
||||
}
|
||||
|
||||
|
||||
void DxvkImageView::createView(VkImageViewType type, uint32_t numLayers) {
|
||||
VkImageSubresourceRange subresourceRange;
|
||||
subresourceRange.aspectMask = info.aspect;
|
||||
subresourceRange.baseMipLevel = info.minLevel;
|
||||
subresourceRange.levelCount = info.numLevels;
|
||||
subresourceRange.baseArrayLayer = info.minLayer;
|
||||
subresourceRange.layerCount = info.numLayers;
|
||||
subresourceRange.aspectMask = m_info.aspect;
|
||||
subresourceRange.baseMipLevel = m_info.minLevel;
|
||||
subresourceRange.levelCount = m_info.numLevels;
|
||||
subresourceRange.baseArrayLayer = m_info.minLayer;
|
||||
subresourceRange.layerCount = numLayers;
|
||||
|
||||
VkImageViewCreateInfo viewInfo;
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.pNext = nullptr;
|
||||
viewInfo.flags = 0;
|
||||
viewInfo.image = image->handle();
|
||||
viewInfo.viewType = info.type;
|
||||
viewInfo.format = info.format;
|
||||
viewInfo.components = info.swizzle;
|
||||
viewInfo.image = m_image->handle();
|
||||
viewInfo.viewType = type;
|
||||
viewInfo.format = m_info.format;
|
||||
viewInfo.components = m_info.swizzle;
|
||||
viewInfo.subresourceRange = subresourceRange;
|
||||
|
||||
if (m_vkd->vkCreateImageView(m_vkd->device(),
|
||||
&viewInfo, nullptr, &m_view) != VK_SUCCESS) {
|
||||
&viewInfo, nullptr, &m_views[type]) != VK_SUCCESS) {
|
||||
throw DxvkError(str::format(
|
||||
"DxvkImageView: Failed to create image view:"
|
||||
"\n View type: ", viewInfo.viewType,
|
||||
@ -117,23 +167,17 @@ namespace dxvk {
|
||||
"\n Array layers: ", viewInfo.subresourceRange.baseArrayLayer, " - ",
|
||||
viewInfo.subresourceRange.layerCount,
|
||||
"\n Image properties:",
|
||||
"\n Type: ", image->info().type,
|
||||
"\n Format: ", image->info().format,
|
||||
"\n Extent: ", "(", image->info().extent.width,
|
||||
",", image->info().extent.height,
|
||||
",", image->info().extent.depth, ")",
|
||||
"\n Mip levels: ", image->info().mipLevels,
|
||||
"\n Array layers: ", image->info().numLayers,
|
||||
"\n Samples: ", image->info().sampleCount,
|
||||
"\n Usage: ", std::hex, image->info().usage,
|
||||
"\n Tiling: ", image->info().tiling));
|
||||
"\n Type: ", m_image->info().type,
|
||||
"\n Format: ", m_image->info().format,
|
||||
"\n Extent: ", "(", m_image->info().extent.width,
|
||||
",", m_image->info().extent.height,
|
||||
",", m_image->info().extent.depth, ")",
|
||||
"\n Mip levels: ", m_image->info().mipLevels,
|
||||
"\n Array layers: ", m_image->info().numLayers,
|
||||
"\n Samples: ", m_image->info().sampleCount,
|
||||
"\n Usage: ", std::hex, m_image->info().usage,
|
||||
"\n Tiling: ", m_image->info().tiling));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxvkImageView::~DxvkImageView() {
|
||||
m_vkd->vkDestroyImageView(
|
||||
m_vkd->device(), m_view, nullptr);
|
||||
}
|
||||
|
||||
}
|
@ -232,7 +232,7 @@ namespace dxvk {
|
||||
* \brief DXVK image view
|
||||
*/
|
||||
class DxvkImageView : public DxvkResource {
|
||||
|
||||
constexpr static uint32_t ViewCount = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY + 1;
|
||||
public:
|
||||
|
||||
DxvkImageView(
|
||||
@ -243,21 +243,34 @@ namespace dxvk {
|
||||
~DxvkImageView();
|
||||
|
||||
/**
|
||||
* \brief Image view handle
|
||||
* \brief Image view handle for the default type
|
||||
*
|
||||
* Internal use only.
|
||||
* The default view type is guaranteed to be
|
||||
* supported by the image view, and should be
|
||||
* preferred over picking a different type.
|
||||
* \returns Image view handle
|
||||
*/
|
||||
VkImageView handle() const {
|
||||
return m_view;
|
||||
return handle(m_info.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Image view handle for a given view type
|
||||
*
|
||||
* If the view does not support the requested image
|
||||
* view type, \c VK_NULL_HANDLE will be returned.
|
||||
* \param [in] viewType The requested view type
|
||||
* \returns The image view handle
|
||||
*/
|
||||
VkImageView handle(VkImageViewType viewType) const {
|
||||
return m_views[viewType];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Image view type
|
||||
*
|
||||
* Convenience method to query the
|
||||
* view type in order to check for
|
||||
* resource compatibility.
|
||||
* Convenience method to query the view type
|
||||
* in order to check for resource compatibility.
|
||||
* \returns Image view type
|
||||
*/
|
||||
VkImageViewType type() const {
|
||||
@ -272,6 +285,14 @@ namespace dxvk {
|
||||
return m_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Image handle
|
||||
* \returns Image handle
|
||||
*/
|
||||
VkImage imageHandle() const {
|
||||
return m_image->handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Image properties
|
||||
* \returns Image properties
|
||||
@ -289,8 +310,8 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Image
|
||||
* \returns Image
|
||||
* \brief Image object
|
||||
* \returns Image object
|
||||
*/
|
||||
Rc<DxvkImage> image() const {
|
||||
return m_image;
|
||||
@ -336,7 +357,9 @@ namespace dxvk {
|
||||
Rc<DxvkImage> m_image;
|
||||
|
||||
DxvkImageViewCreateInfo m_info;
|
||||
VkImageView m_view;
|
||||
VkImageView m_views[ViewCount];
|
||||
|
||||
void createView(VkImageViewType type, uint32_t numLayers);
|
||||
|
||||
};
|
||||
|
||||
|
554
src/dxvk/dxvk_meta_mipgen.cpp
Normal file
554
src/dxvk/dxvk_meta_mipgen.cpp
Normal file
@ -0,0 +1,554 @@
|
||||
#include "dxvk_meta_mipgen.h"
|
||||
|
||||
#include <dxvk_mipgen_vert.h>
|
||||
#include <dxvk_mipgen_geom.h>
|
||||
#include <dxvk_mipgen_frag_1d.h>
|
||||
#include <dxvk_mipgen_frag_2d.h>
|
||||
#include <dxvk_mipgen_frag_3d.h>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkMetaMipGenRenderPass::DxvkMetaMipGenRenderPass(
|
||||
const Rc<vk::DeviceFn>& vkd,
|
||||
const Rc<DxvkImageView>& view)
|
||||
: m_vkd(vkd), m_view(view), m_renderPass(createRenderPass()) {
|
||||
// Determine view type based on image type
|
||||
const std::array<std::pair<VkImageViewType, VkImageViewType>, 3> viewTypes = {{
|
||||
{ VK_IMAGE_VIEW_TYPE_1D_ARRAY, VK_IMAGE_VIEW_TYPE_1D_ARRAY },
|
||||
{ VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_VIEW_TYPE_2D_ARRAY },
|
||||
{ VK_IMAGE_VIEW_TYPE_3D, VK_IMAGE_VIEW_TYPE_2D_ARRAY },
|
||||
}};
|
||||
|
||||
m_srcViewType = viewTypes.at(uint32_t(view->imageInfo().type)).first;
|
||||
m_dstViewType = viewTypes.at(uint32_t(view->imageInfo().type)).second;
|
||||
|
||||
// Create image views and framebuffers
|
||||
m_passes.resize(view->info().numLevels - 1);
|
||||
|
||||
for (uint32_t i = 0; i < m_passes.size(); i++)
|
||||
m_passes.at(i) = this->createFramebuffer(i);
|
||||
}
|
||||
|
||||
|
||||
DxvkMetaMipGenRenderPass::~DxvkMetaMipGenRenderPass() {
|
||||
for (const auto& pass : m_passes) {
|
||||
m_vkd->vkDestroyFramebuffer(m_vkd->device(), pass.framebuffer, nullptr);
|
||||
m_vkd->vkDestroyImageView(m_vkd->device(), pass.dstView, nullptr);
|
||||
m_vkd->vkDestroyImageView(m_vkd->device(), pass.srcView, nullptr);
|
||||
}
|
||||
|
||||
m_vkd->vkDestroyRenderPass(m_vkd->device(), m_renderPass, nullptr);
|
||||
}
|
||||
|
||||
|
||||
VkExtent3D DxvkMetaMipGenRenderPass::passExtent(uint32_t passId) const {
|
||||
VkExtent3D extent = m_view->mipLevelExtent(passId + 1);
|
||||
|
||||
if (m_view->imageInfo().type != VK_IMAGE_TYPE_3D)
|
||||
extent.depth = m_view->info().numLayers;
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
|
||||
VkRenderPass DxvkMetaMipGenRenderPass::createRenderPass() const {
|
||||
std::array<VkSubpassDependency, 2> subpassDeps = {{
|
||||
{ VK_SUBPASS_EXTERNAL, 0,
|
||||
m_view->imageInfo().stages,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
m_view->imageInfo().access,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0 },
|
||||
{ 0, VK_SUBPASS_EXTERNAL,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
m_view->imageInfo().stages,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
m_view->imageInfo().access, 0 },
|
||||
}};
|
||||
|
||||
VkAttachmentDescription attachment;
|
||||
attachment.flags = 0;
|
||||
attachment.format = m_view->info().format;
|
||||
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachment.finalLayout = m_view->imageInfo().layout;
|
||||
|
||||
VkAttachmentReference attachmentRef;
|
||||
attachmentRef.attachment = 0;
|
||||
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass;
|
||||
subpass.flags = 0;
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = nullptr;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &attachmentRef;
|
||||
subpass.pResolveAttachments = nullptr;
|
||||
subpass.pDepthStencilAttachment = nullptr;
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = nullptr;
|
||||
|
||||
VkRenderPassCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.attachmentCount = 1;
|
||||
info.pAttachments = &attachment;
|
||||
info.subpassCount = 1;
|
||||
info.pSubpasses = &subpass;
|
||||
info.dependencyCount = subpassDeps.size();
|
||||
info.pDependencies = subpassDeps.data();
|
||||
|
||||
VkRenderPass result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create render pass");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxvkMetaMipGenPass DxvkMetaMipGenRenderPass::createFramebuffer(uint32_t pass) const {
|
||||
DxvkMetaMipGenPass result;
|
||||
result.srcView = VK_NULL_HANDLE;
|
||||
result.dstView = VK_NULL_HANDLE;
|
||||
result.framebuffer = VK_NULL_HANDLE;
|
||||
|
||||
// Common image view info
|
||||
VkImageViewCreateInfo viewInfo;
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.pNext = nullptr;
|
||||
viewInfo.flags = 0;
|
||||
viewInfo.image = m_view->imageHandle();
|
||||
viewInfo.format = m_view->info().format;
|
||||
viewInfo.components = {
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
|
||||
|
||||
// Create source image view, which points to
|
||||
// the one mip level we're going to sample.
|
||||
VkImageSubresourceRange srcSubresources;
|
||||
srcSubresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
srcSubresources.baseMipLevel = m_view->info().minLevel + pass;
|
||||
srcSubresources.levelCount = 1;
|
||||
srcSubresources.baseArrayLayer = m_view->info().minLayer;
|
||||
srcSubresources.layerCount = m_view->info().numLayers;
|
||||
|
||||
viewInfo.viewType = m_srcViewType;
|
||||
viewInfo.subresourceRange = srcSubresources;
|
||||
|
||||
if (m_vkd->vkCreateImageView(m_vkd->device(), &viewInfo, nullptr, &result.srcView) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create source image view");
|
||||
|
||||
// Create destination image view, which points
|
||||
// to the mip level we're going to render to.
|
||||
VkExtent3D dstExtent = m_view->mipLevelExtent(pass + 1);
|
||||
|
||||
VkImageSubresourceRange dstSubresources;
|
||||
dstSubresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
dstSubresources.baseMipLevel = m_view->info().minLevel + pass + 1;
|
||||
dstSubresources.levelCount = 1;
|
||||
|
||||
if (m_view->imageInfo().type != VK_IMAGE_TYPE_3D) {
|
||||
dstSubresources.baseArrayLayer = m_view->info().minLayer;
|
||||
dstSubresources.layerCount = m_view->info().numLayers;
|
||||
} else {
|
||||
dstSubresources.baseArrayLayer = 0;
|
||||
dstSubresources.layerCount = dstExtent.depth;
|
||||
}
|
||||
|
||||
viewInfo.viewType = m_dstViewType;
|
||||
viewInfo.subresourceRange = dstSubresources;
|
||||
|
||||
if (m_vkd->vkCreateImageView(m_vkd->device(), &viewInfo, nullptr, &result.dstView) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create target image view");
|
||||
|
||||
// Create framebuffer using the destination
|
||||
// image view as its color attachment.
|
||||
VkFramebufferCreateInfo fboInfo;
|
||||
fboInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
fboInfo.pNext = nullptr;
|
||||
fboInfo.flags = 0;
|
||||
fboInfo.renderPass = m_renderPass;
|
||||
fboInfo.attachmentCount = 1;
|
||||
fboInfo.pAttachments = &result.dstView;
|
||||
fboInfo.width = dstExtent.width;
|
||||
fboInfo.height = dstExtent.height;
|
||||
fboInfo.layers = dstSubresources.layerCount;
|
||||
|
||||
if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &fboInfo, nullptr, &result.framebuffer) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenRenderPass: Failed to create target framebuffer");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxvkMetaMipGenObjects::DxvkMetaMipGenObjects(const Rc<vk::DeviceFn>& vkd)
|
||||
: m_vkd (vkd),
|
||||
m_sampler (createSampler()),
|
||||
m_shaderVert (createShaderModule(dxvk_mipgen_vert)),
|
||||
m_shaderGeom (createShaderModule(dxvk_mipgen_geom)),
|
||||
m_shaderFrag1D(createShaderModule(dxvk_mipgen_frag_1d)),
|
||||
m_shaderFrag2D(createShaderModule(dxvk_mipgen_frag_2d)),
|
||||
m_shaderFrag3D(createShaderModule(dxvk_mipgen_frag_3d)) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
DxvkMetaMipGenObjects::~DxvkMetaMipGenObjects() {
|
||||
for (const auto& pair : m_renderPasses)
|
||||
m_vkd->vkDestroyRenderPass(m_vkd->device(), pair.second, nullptr);
|
||||
|
||||
for (const auto& pair : m_pipelines) {
|
||||
m_vkd->vkDestroyPipeline(m_vkd->device(), pair.second.pipeHandle, nullptr);
|
||||
m_vkd->vkDestroyPipelineLayout(m_vkd->device(), pair.second.pipeLayout, nullptr);
|
||||
m_vkd->vkDestroyDescriptorSetLayout (m_vkd->device(), pair.second.dsetLayout, nullptr);
|
||||
}
|
||||
|
||||
m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFrag3D, nullptr);
|
||||
m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFrag2D, nullptr);
|
||||
m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderFrag1D, nullptr);
|
||||
m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderGeom, nullptr);
|
||||
m_vkd->vkDestroyShaderModule(m_vkd->device(), m_shaderVert, nullptr);
|
||||
|
||||
m_vkd->vkDestroySampler(m_vkd->device(), m_sampler, nullptr);
|
||||
}
|
||||
|
||||
|
||||
DxvkMetaMipGenPipeline DxvkMetaMipGenObjects::getPipeline(
|
||||
VkImageViewType viewType,
|
||||
VkFormat viewFormat) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
DxvkMetaMipGenPipelineKey key;
|
||||
key.viewType = viewType;
|
||||
key.viewFormat = viewFormat;
|
||||
|
||||
auto entry = m_pipelines.find(key);
|
||||
if (entry != m_pipelines.end())
|
||||
return entry->second;
|
||||
|
||||
DxvkMetaMipGenPipeline pipeline = this->createPipeline(key);
|
||||
m_pipelines.insert({ key, pipeline });
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
VkRenderPass DxvkMetaMipGenObjects::getRenderPass(VkFormat viewFormat) {
|
||||
auto entry = m_renderPasses.find(viewFormat);
|
||||
if (entry != m_renderPasses.end())
|
||||
return entry->second;
|
||||
|
||||
VkRenderPass renderPass = this->createRenderPass(viewFormat);
|
||||
m_renderPasses.insert({ viewFormat, renderPass });
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
|
||||
VkSampler DxvkMetaMipGenObjects::createSampler() const {
|
||||
VkSamplerCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.magFilter = VK_FILTER_LINEAR;
|
||||
info.minFilter = VK_FILTER_LINEAR;
|
||||
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
info.mipLodBias = 0.0f;
|
||||
info.anisotropyEnable = VK_FALSE;
|
||||
info.maxAnisotropy = 1.0f;
|
||||
info.compareEnable = VK_FALSE;
|
||||
info.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
info.minLod = 0.0f;
|
||||
info.maxLod = 0.0f;
|
||||
info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||
info.unnormalizedCoordinates = VK_FALSE;
|
||||
|
||||
VkSampler result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreateSampler(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenObjects: Failed to create sampler");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
VkShaderModule DxvkMetaMipGenObjects::createShaderModule(const SpirvCodeBuffer& code) const {
|
||||
VkShaderModuleCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.codeSize = code.size();
|
||||
info.pCode = code.data();
|
||||
|
||||
VkShaderModule result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreateShaderModule(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenObjects: Failed to create shader module");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxvkMetaMipGenPipeline DxvkMetaMipGenObjects::createPipeline(
|
||||
const DxvkMetaMipGenPipelineKey& key) {
|
||||
DxvkMetaMipGenPipeline pipe;
|
||||
pipe.dsetLayout = this->createDescriptorSetLayout(key.viewType);
|
||||
pipe.pipeLayout = this->createPipelineLayout(pipe.dsetLayout);
|
||||
pipe.pipeHandle = this->createPipeline(key.viewType, pipe.pipeLayout,
|
||||
this->getRenderPass(key.viewFormat));
|
||||
return pipe;
|
||||
}
|
||||
|
||||
|
||||
VkRenderPass DxvkMetaMipGenObjects::createRenderPass(
|
||||
VkFormat format) const {
|
||||
VkAttachmentDescription attachment;
|
||||
attachment.flags = 0;
|
||||
attachment.format = format;
|
||||
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkAttachmentReference attachmentRef;
|
||||
attachmentRef.attachment = 0;
|
||||
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass;
|
||||
subpass.flags = 0;
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = nullptr;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &attachmentRef;
|
||||
subpass.pResolveAttachments = nullptr;
|
||||
subpass.pDepthStencilAttachment = nullptr;
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = nullptr;
|
||||
|
||||
VkRenderPassCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.attachmentCount = 1;
|
||||
info.pAttachments = &attachment;
|
||||
info.subpassCount = 1;
|
||||
info.pSubpasses = &subpass;
|
||||
info.dependencyCount = 0;
|
||||
info.pDependencies = nullptr;
|
||||
|
||||
VkRenderPass result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenObjects: Failed to create render pass");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
VkDescriptorSetLayout DxvkMetaMipGenObjects::createDescriptorSetLayout(
|
||||
VkImageViewType viewType) const {
|
||||
VkDescriptorSetLayoutBinding binding;
|
||||
binding.binding = 0;
|
||||
binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
binding.descriptorCount = 1;
|
||||
binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
binding.pImmutableSamplers = &m_sampler;
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.bindingCount = 1;
|
||||
info.pBindings = &binding;
|
||||
|
||||
VkDescriptorSetLayout result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenObjects: Failed to create descriptor set layout");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
VkPipelineLayout DxvkMetaMipGenObjects::createPipelineLayout(
|
||||
VkDescriptorSetLayout descriptorSetLayout) const {
|
||||
VkPushConstantRange pushRange;
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushRange.offset = 0;
|
||||
pushRange.size = sizeof(DxvkMetaMipGenPushConstants);
|
||||
|
||||
VkPipelineLayoutCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.setLayoutCount = 1;
|
||||
info.pSetLayouts = &descriptorSetLayout;
|
||||
info.pushConstantRangeCount = 1;
|
||||
info.pPushConstantRanges = &pushRange;
|
||||
|
||||
VkPipelineLayout result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreatePipelineLayout(m_vkd->device(), &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenObjects: Failed to create pipeline layout");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
VkPipeline DxvkMetaMipGenObjects::createPipeline(
|
||||
VkImageViewType imageViewType,
|
||||
VkPipelineLayout pipelineLayout,
|
||||
VkRenderPass renderPass) const {
|
||||
std::array<VkPipelineShaderStageCreateInfo, 3> stages;
|
||||
|
||||
VkPipelineShaderStageCreateInfo& vsStage = stages[0];
|
||||
vsStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vsStage.pNext = nullptr;
|
||||
vsStage.flags = 0;
|
||||
vsStage.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsStage.module = m_shaderVert;
|
||||
vsStage.pName = "main";
|
||||
vsStage.pSpecializationInfo = nullptr;
|
||||
|
||||
VkPipelineShaderStageCreateInfo& gsStage = stages[1];
|
||||
gsStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
gsStage.pNext = nullptr;
|
||||
gsStage.flags = 0;
|
||||
gsStage.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
gsStage.module = m_shaderGeom;
|
||||
gsStage.pName = "main";
|
||||
gsStage.pSpecializationInfo = nullptr;
|
||||
|
||||
VkPipelineShaderStageCreateInfo& psStage = stages[2];
|
||||
psStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
psStage.pNext = nullptr;
|
||||
psStage.flags = 0;
|
||||
psStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
psStage.module = VK_NULL_HANDLE;
|
||||
psStage.pName = "main";
|
||||
psStage.pSpecializationInfo = nullptr;
|
||||
|
||||
switch (imageViewType) {
|
||||
case VK_IMAGE_VIEW_TYPE_1D_ARRAY: psStage.module = m_shaderFrag1D; break;
|
||||
case VK_IMAGE_VIEW_TYPE_2D_ARRAY: psStage.module = m_shaderFrag2D; break;
|
||||
case VK_IMAGE_VIEW_TYPE_3D: psStage.module = m_shaderFrag3D; break;
|
||||
default: throw DxvkError("DxvkMetaMipGenObjects: Invalid view type");
|
||||
}
|
||||
|
||||
std::array<VkDynamicState, 2> dynStates = {{
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
}};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynState;
|
||||
dynState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynState.pNext = nullptr;
|
||||
dynState.flags = 0;
|
||||
dynState.dynamicStateCount = dynStates.size();
|
||||
dynState.pDynamicStates = dynStates.data();
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo viState;
|
||||
viState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
viState.pNext = nullptr;
|
||||
viState.flags = 0;
|
||||
viState.vertexBindingDescriptionCount = 0;
|
||||
viState.pVertexBindingDescriptions = nullptr;
|
||||
viState.vertexAttributeDescriptionCount = 0;
|
||||
viState.pVertexAttributeDescriptions = nullptr;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo iaState;
|
||||
iaState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
iaState.pNext = nullptr;
|
||||
iaState.flags = 0;
|
||||
iaState.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
iaState.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vpState;
|
||||
vpState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
vpState.pNext = nullptr;
|
||||
vpState.flags = 0;
|
||||
vpState.viewportCount = 1;
|
||||
vpState.pViewports = nullptr;
|
||||
vpState.scissorCount = 1;
|
||||
vpState.pScissors = nullptr;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rsState;
|
||||
rsState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rsState.pNext = nullptr;
|
||||
rsState.flags = 0;
|
||||
rsState.depthClampEnable = VK_TRUE;
|
||||
rsState.rasterizerDiscardEnable = VK_FALSE;
|
||||
rsState.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rsState.cullMode = VK_CULL_MODE_NONE;
|
||||
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rsState.depthBiasEnable = VK_FALSE;
|
||||
rsState.depthBiasConstantFactor = 0.0f;
|
||||
rsState.depthBiasClamp = 0.0f;
|
||||
rsState.depthBiasSlopeFactor = 0.0f;
|
||||
rsState.lineWidth = 1.0f;
|
||||
|
||||
uint32_t msMask = 0xFFFFFFFF;
|
||||
VkPipelineMultisampleStateCreateInfo msState;
|
||||
msState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
msState.pNext = nullptr;
|
||||
msState.flags = 0;
|
||||
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
msState.sampleShadingEnable = VK_FALSE;
|
||||
msState.minSampleShading = 1.0f;
|
||||
msState.pSampleMask = &msMask;
|
||||
msState.alphaToCoverageEnable = VK_FALSE;
|
||||
msState.alphaToOneEnable = VK_FALSE;
|
||||
|
||||
VkPipelineColorBlendAttachmentState cbAttachment;
|
||||
cbAttachment.blendEnable = VK_FALSE;
|
||||
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cbState;
|
||||
cbState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
cbState.pNext = nullptr;
|
||||
cbState.flags = 0;
|
||||
cbState.logicOpEnable = VK_FALSE;
|
||||
cbState.logicOp = VK_LOGIC_OP_NO_OP;
|
||||
cbState.attachmentCount = 1;
|
||||
cbState.pAttachments = &cbAttachment;
|
||||
|
||||
for (uint32_t i = 0; i < 4; i++)
|
||||
cbState.blendConstants[i] = 0.0f;
|
||||
|
||||
VkGraphicsPipelineCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
info.stageCount = stages.size();
|
||||
info.pStages = stages.data();
|
||||
info.pVertexInputState = &viState;
|
||||
info.pInputAssemblyState = &iaState;
|
||||
info.pTessellationState = nullptr;
|
||||
info.pViewportState = &vpState;
|
||||
info.pRasterizationState = &rsState;
|
||||
info.pMultisampleState = &msState;
|
||||
info.pColorBlendState = &cbState;
|
||||
info.pDepthStencilState = nullptr;
|
||||
info.pDynamicState = &dynState;
|
||||
info.layout = pipelineLayout;
|
||||
info.renderPass = renderPass;
|
||||
info.subpass = 0;
|
||||
info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
info.basePipelineIndex = -1;
|
||||
|
||||
VkPipeline result = VK_NULL_HANDLE;
|
||||
if (m_vkd->vkCreateGraphicsPipelines(m_vkd->device(), VK_NULL_HANDLE, 1, &info, nullptr, &result) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkMetaMipGenObjects: Failed to create graphics pipeline");
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
232
src/dxvk/dxvk_meta_mipgen.h
Normal file
232
src/dxvk/dxvk_meta_mipgen.h
Normal file
@ -0,0 +1,232 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../spirv/spirv_code_buffer.h"
|
||||
|
||||
#include "dxvk_hash.h"
|
||||
#include "dxvk_image.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Push constant data
|
||||
*/
|
||||
struct DxvkMetaMipGenPushConstants {
|
||||
uint32_t layerCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Mip map generation pipeline key
|
||||
*
|
||||
* We have to create pipelines for each
|
||||
* combination of source image view type
|
||||
* and image format.
|
||||
*/
|
||||
struct DxvkMetaMipGenPipelineKey {
|
||||
VkImageViewType viewType;
|
||||
VkFormat viewFormat;
|
||||
|
||||
bool eq(const DxvkMetaMipGenPipelineKey& other) const {
|
||||
return this->viewType == other.viewType
|
||||
&& this->viewFormat == other.viewFormat;
|
||||
}
|
||||
|
||||
size_t hash() const {
|
||||
DxvkHashState result;
|
||||
result.add(uint32_t(this->viewType));
|
||||
result.add(uint32_t(this->viewFormat));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Mip map generation pipeline
|
||||
*
|
||||
* Stores the objects for a single pipeline
|
||||
* that is used for mipmap generation.
|
||||
*/
|
||||
struct DxvkMetaMipGenPipeline {
|
||||
VkDescriptorSetLayout dsetLayout;
|
||||
VkPipelineLayout pipeLayout;
|
||||
VkPipeline pipeHandle;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Mip map generation framebuffer
|
||||
*
|
||||
* Stores the image views and framebuffer
|
||||
* handle used to generate one mip level.
|
||||
*/
|
||||
struct DxvkMetaMipGenPass {
|
||||
VkImageView srcView;
|
||||
VkImageView dstView;
|
||||
VkFramebuffer framebuffer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Mip map generation render pass
|
||||
*
|
||||
* Stores image views, framebuffer objects and
|
||||
* a render pass object for mip map generation.
|
||||
* This must be created per image view.
|
||||
*/
|
||||
class DxvkMetaMipGenRenderPass : public DxvkResource {
|
||||
|
||||
public:
|
||||
|
||||
DxvkMetaMipGenRenderPass(
|
||||
const Rc<vk::DeviceFn>& vkd,
|
||||
const Rc<DxvkImageView>& view);
|
||||
|
||||
~DxvkMetaMipGenRenderPass();
|
||||
|
||||
/**
|
||||
* \brief Render pass handle
|
||||
* \returns Render pass handle
|
||||
*/
|
||||
VkRenderPass renderPass() const {
|
||||
return m_renderPass;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Source image view type
|
||||
*
|
||||
* Use this to figure out which type the
|
||||
* resource descriptor needs to have.
|
||||
* \returns Source image view type
|
||||
*/
|
||||
VkImageViewType viewType() const {
|
||||
return m_srcViewType;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Render pass count
|
||||
*
|
||||
* Number of mip levels to generate.
|
||||
* \returns Render pass count
|
||||
*/
|
||||
uint32_t passCount() const {
|
||||
return m_passes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Framebuffer handles
|
||||
*
|
||||
* Returns image view and framebuffer handles
|
||||
* required to generate a single mip level.
|
||||
* \param [in] pass Render pass index
|
||||
* \returns Object handles for the given pass
|
||||
*/
|
||||
DxvkMetaMipGenPass pass(uint32_t passId) const {
|
||||
return m_passes.at(passId);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Framebuffer size for a given pass
|
||||
*
|
||||
* Stores the width, height, and layer count
|
||||
* of the framebuffer for the given pass ID.
|
||||
*/
|
||||
VkExtent3D passExtent(uint32_t passId) const;
|
||||
|
||||
private:
|
||||
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
Rc<DxvkImageView> m_view;
|
||||
|
||||
VkRenderPass m_renderPass;
|
||||
|
||||
VkImageViewType m_srcViewType;
|
||||
VkImageViewType m_dstViewType;
|
||||
|
||||
std::vector<DxvkMetaMipGenPass> m_passes;
|
||||
|
||||
VkRenderPass createRenderPass() const;
|
||||
|
||||
DxvkMetaMipGenPass createFramebuffer(uint32_t pass) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Mip map generation objects
|
||||
*
|
||||
* Stores render pass objects and pipelines used
|
||||
* to generate mip maps. Due to Vulkan API design
|
||||
* decisions, we have to create one render pass
|
||||
* and pipeline object per image format used.
|
||||
*/
|
||||
class DxvkMetaMipGenObjects : public RcObject {
|
||||
|
||||
public:
|
||||
|
||||
DxvkMetaMipGenObjects(const Rc<vk::DeviceFn>& vkd);
|
||||
~DxvkMetaMipGenObjects();
|
||||
|
||||
/**
|
||||
* \brief Creates a mip map generation pipeline
|
||||
*
|
||||
* \param [in] viewType Source image view type
|
||||
* \param [in] viewFormat Image view format
|
||||
* \returns The mip map generation pipeline
|
||||
*/
|
||||
DxvkMetaMipGenPipeline getPipeline(
|
||||
VkImageViewType viewType,
|
||||
VkFormat viewFormat);
|
||||
|
||||
private:
|
||||
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
|
||||
VkSampler m_sampler;
|
||||
|
||||
VkShaderModule m_shaderVert;
|
||||
VkShaderModule m_shaderGeom;
|
||||
VkShaderModule m_shaderFrag1D;
|
||||
VkShaderModule m_shaderFrag2D;
|
||||
VkShaderModule m_shaderFrag3D;
|
||||
|
||||
std::mutex m_mutex;
|
||||
|
||||
std::unordered_map<
|
||||
VkFormat,
|
||||
VkRenderPass> m_renderPasses;
|
||||
|
||||
std::unordered_map<
|
||||
DxvkMetaMipGenPipelineKey,
|
||||
DxvkMetaMipGenPipeline,
|
||||
DxvkHash, DxvkEq> m_pipelines;
|
||||
|
||||
VkRenderPass getRenderPass(
|
||||
VkFormat viewFormat);
|
||||
|
||||
VkSampler createSampler() const;
|
||||
|
||||
VkShaderModule createShaderModule(
|
||||
const SpirvCodeBuffer& code) const;
|
||||
|
||||
DxvkMetaMipGenPipeline createPipeline(
|
||||
const DxvkMetaMipGenPipelineKey& key);
|
||||
|
||||
VkRenderPass createRenderPass(
|
||||
VkFormat format) const;
|
||||
|
||||
VkDescriptorSetLayout createDescriptorSetLayout(
|
||||
VkImageViewType viewType) const;
|
||||
|
||||
VkPipelineLayout createPipelineLayout(
|
||||
VkDescriptorSetLayout descriptorSetLayout) const;
|
||||
|
||||
VkPipeline createPipeline(
|
||||
VkImageViewType imageViewType,
|
||||
VkPipelineLayout pipelineLayout,
|
||||
VkRenderPass renderPass) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -4,9 +4,7 @@
|
||||
namespace dxvk {
|
||||
|
||||
DxvkPipelineCompiler::DxvkPipelineCompiler() {
|
||||
// Use ~half the CPU cores for pipeline compilation
|
||||
const uint32_t threadCount = std::max<uint32_t>(
|
||||
1u, std::thread::hardware_concurrency() / 2);
|
||||
constexpr uint32_t threadCount = 1u;
|
||||
|
||||
Logger::debug(str::format(
|
||||
"DxvkPipelineCompiler: Using ", threadCount, " workers"));
|
||||
|
@ -11,6 +11,22 @@ namespace dxvk {
|
||||
|
||||
class DxvkShader;
|
||||
|
||||
/**
|
||||
* \brief Built-in specialization constants
|
||||
*
|
||||
* These specialization constants allow the SPIR-V
|
||||
* shaders to access some pipeline state like D3D
|
||||
* shaders do. They need to be filled in by the
|
||||
* implementation at pipeline compilation time.
|
||||
*/
|
||||
enum class DxvkSpecConstantId : uint32_t {
|
||||
RasterizerSampleCount = 0x10000,
|
||||
|
||||
SpecConstantIdMin = RasterizerSampleCount,
|
||||
SpecConstantIdMax = RasterizerSampleCount,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Shader interface slots
|
||||
*
|
||||
|
41
src/dxvk/dxvk_spec_const.cpp
Normal file
41
src/dxvk/dxvk_spec_const.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "dxvk_spec_const.h"
|
||||
|
||||
#define SET_CONSTANT_ENTRY(specId, member) \
|
||||
this->setConstantEntry(specId, \
|
||||
offsetof(DxvkSpecConstantData, member), \
|
||||
sizeof(DxvkSpecConstantData::member))
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkSpecConstantMap g_specConstantMap;
|
||||
|
||||
DxvkSpecConstantMap::DxvkSpecConstantMap() {
|
||||
SET_CONSTANT_ENTRY(DxvkSpecConstantId::RasterizerSampleCount, rasterizerSampleCount);
|
||||
|
||||
for (uint32_t i = 0; i < MaxNumActiveBindings; i++)
|
||||
this->setBindingEntry(i);
|
||||
}
|
||||
|
||||
|
||||
void DxvkSpecConstantMap::setConstantEntry(
|
||||
DxvkSpecConstantId specId,
|
||||
uint32_t offset,
|
||||
uint32_t size) {
|
||||
VkSpecializationMapEntry entry;
|
||||
entry.constantID = uint32_t(specId);
|
||||
entry.offset = offset;
|
||||
entry.size = size;
|
||||
m_mapEntries[uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin)] = entry;
|
||||
}
|
||||
|
||||
|
||||
void DxvkSpecConstantMap::setBindingEntry(
|
||||
uint32_t binding) {
|
||||
VkSpecializationMapEntry entry;
|
||||
entry.constantID = binding;
|
||||
entry.offset = sizeof(VkBool32) * binding + offsetof(DxvkSpecConstantData, activeBindings);
|
||||
entry.size = sizeof(VkBool32);
|
||||
m_mapEntries[MaxNumSpecConstants + binding] = entry;
|
||||
}
|
||||
|
||||
}
|
72
src/dxvk/dxvk_spec_const.h
Normal file
72
src/dxvk/dxvk_spec_const.h
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "dxvk_limits.h"
|
||||
#include "dxvk_shader.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
constexpr uint32_t MaxNumSpecConstants = 1
|
||||
+ uint32_t(DxvkSpecConstantId::SpecConstantIdMax)
|
||||
- uint32_t(DxvkSpecConstantId::SpecConstantIdMin);
|
||||
|
||||
/**
|
||||
* \brief Spec costant data
|
||||
*
|
||||
* The values are derived from the pipeline
|
||||
* state vector so that they can be used by
|
||||
* the shaders.
|
||||
*/
|
||||
struct DxvkSpecConstantData {
|
||||
uint32_t rasterizerSampleCount;
|
||||
VkBool32 activeBindings[MaxNumActiveBindings];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Spec constant map
|
||||
*
|
||||
* Stores the specialization constant map.
|
||||
* This can be passed to Vulkan when compiling
|
||||
* both graphics and compute pipelines.
|
||||
*/
|
||||
class DxvkSpecConstantMap {
|
||||
|
||||
public:
|
||||
|
||||
DxvkSpecConstantMap();
|
||||
|
||||
/**
|
||||
* \brief Map entry count
|
||||
*
|
||||
* \param [in] bindingCount Number of active bindings
|
||||
* \returns The number of map entries to read
|
||||
*/
|
||||
uint32_t mapEntryCount() const {
|
||||
return m_mapEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Map entry data
|
||||
* \returns Map entries
|
||||
*/
|
||||
const VkSpecializationMapEntry* mapEntryData() const {
|
||||
return m_mapEntries.data();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::array<VkSpecializationMapEntry, MaxNumSpecConstants + MaxNumActiveBindings> m_mapEntries;
|
||||
|
||||
void setConstantEntry(
|
||||
DxvkSpecConstantId specId,
|
||||
uint32_t offset,
|
||||
uint32_t size);
|
||||
|
||||
void setBindingEntry(
|
||||
uint32_t binding);
|
||||
|
||||
};
|
||||
|
||||
extern DxvkSpecConstantMap g_specConstantMap;
|
||||
|
||||
}
|
@ -12,6 +12,12 @@ dxvk_shaders = files([
|
||||
'shaders/dxvk_clear_image3d_u.comp',
|
||||
'shaders/dxvk_clear_image3d_f.comp',
|
||||
|
||||
'shaders/dxvk_mipgen_vert.vert',
|
||||
'shaders/dxvk_mipgen_geom.geom',
|
||||
'shaders/dxvk_mipgen_frag_1d.frag',
|
||||
'shaders/dxvk_mipgen_frag_2d.frag',
|
||||
'shaders/dxvk_mipgen_frag_3d.frag',
|
||||
|
||||
'hud/shaders/hud_line.frag',
|
||||
'hud/shaders/hud_text.frag',
|
||||
'hud/shaders/hud_vert.vert',
|
||||
@ -41,6 +47,7 @@ dxvk_src = files([
|
||||
'dxvk_main.cpp',
|
||||
'dxvk_memory.cpp',
|
||||
'dxvk_meta_clear.cpp',
|
||||
'dxvk_meta_mipgen.cpp',
|
||||
'dxvk_meta_resolve.cpp',
|
||||
'dxvk_openvr.cpp',
|
||||
'dxvk_pipecache.cpp',
|
||||
@ -55,6 +62,7 @@ dxvk_src = files([
|
||||
'dxvk_resource.cpp',
|
||||
'dxvk_sampler.cpp',
|
||||
'dxvk_shader.cpp',
|
||||
'dxvk_spec_const.cpp',
|
||||
'dxvk_staging.cpp',
|
||||
'dxvk_stats.cpp',
|
||||
'dxvk_surface.cpp',
|
||||
|
11
src/dxvk/shaders/dxvk_mipgen_frag_1d.frag
Normal file
11
src/dxvk/shaders/dxvk_mipgen_frag_1d.frag
Normal file
@ -0,0 +1,11 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform sampler1DArray s_texture;
|
||||
|
||||
layout(location = 0) in vec3 i_pos;
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = texture(s_texture, i_pos.xz);
|
||||
}
|
11
src/dxvk/shaders/dxvk_mipgen_frag_2d.frag
Normal file
11
src/dxvk/shaders/dxvk_mipgen_frag_2d.frag
Normal file
@ -0,0 +1,11 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform sampler2DArray s_texture;
|
||||
|
||||
layout(location = 0) in vec3 i_pos;
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = texture(s_texture, i_pos);
|
||||
}
|
17
src/dxvk/shaders/dxvk_mipgen_frag_3d.frag
Normal file
17
src/dxvk/shaders/dxvk_mipgen_frag_3d.frag
Normal file
@ -0,0 +1,17 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform sampler3D s_texture;
|
||||
|
||||
layout(location = 0) in vec3 i_pos;
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_block {
|
||||
uint p_layer_count;
|
||||
};
|
||||
|
||||
void main() {
|
||||
o_color = texture(s_texture, vec3(i_pos.xy,
|
||||
(i_pos.z + 0.5f) / float(p_layer_count)));
|
||||
}
|
25
src/dxvk/shaders/dxvk_mipgen_geom.geom
Normal file
25
src/dxvk/shaders/dxvk_mipgen_geom.geom
Normal file
@ -0,0 +1,25 @@
|
||||
#version 450
|
||||
|
||||
layout(points) in;
|
||||
layout(triangle_strip, max_vertices = 4) out;
|
||||
|
||||
layout(location = 0) in int i_instance[1];
|
||||
layout(location = 0) out vec3 o_pos;
|
||||
|
||||
const vec4 g_vpos[4] = {
|
||||
vec4(-1.0f, -1.0f, 0.0f, 1.0f),
|
||||
vec4(-1.0f, 1.0f, 0.0f, 1.0f),
|
||||
vec4( 1.0f, -1.0f, 0.0f, 1.0f),
|
||||
vec4( 1.0f, 1.0f, 0.0f, 1.0f),
|
||||
};
|
||||
|
||||
void main() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
o_pos = vec3(0.5f + 0.5f * g_vpos[i].xy, float(i_instance[0]));
|
||||
gl_Position = g_vpos[i];
|
||||
gl_Layer = i_instance[0];
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
EndPrimitive();
|
||||
}
|
8
src/dxvk/shaders/dxvk_mipgen_vert.vert
Normal file
8
src/dxvk/shaders/dxvk_mipgen_vert.vert
Normal file
@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out int o_instance;
|
||||
|
||||
void main() {
|
||||
o_instance = gl_InstanceIndex;
|
||||
gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
@ -92,6 +92,16 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void SpirvModule::setInvocations(
|
||||
uint32_t entryPointId,
|
||||
uint32_t invocations) {
|
||||
m_execModeInfo.putIns (spv::OpExecutionMode, 4);
|
||||
m_execModeInfo.putWord (entryPointId);
|
||||
m_execModeInfo.putWord (spv::ExecutionModeInvocations);
|
||||
m_execModeInfo.putInt32(invocations);
|
||||
}
|
||||
|
||||
|
||||
void SpirvModule::setLocalSize(
|
||||
uint32_t entryPointId,
|
||||
uint32_t x,
|
||||
@ -330,6 +340,19 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::specConst32(
|
||||
uint32_t typeId,
|
||||
uint32_t value) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
|
||||
m_typeConstDefs.putIns (spv::OpSpecConstant, 4);
|
||||
m_typeConstDefs.putWord (typeId);
|
||||
m_typeConstDefs.putWord (resultId);
|
||||
m_typeConstDefs.putWord (value);
|
||||
return resultId;
|
||||
}
|
||||
|
||||
|
||||
void SpirvModule::decorate(
|
||||
uint32_t object,
|
||||
spv::Decoration decoration) {
|
||||
|
@ -79,6 +79,10 @@ namespace dxvk {
|
||||
uint32_t entryPointId,
|
||||
spv::ExecutionMode executionMode);
|
||||
|
||||
void setInvocations(
|
||||
uint32_t entryPointId,
|
||||
uint32_t invocations);
|
||||
|
||||
void setLocalSize(
|
||||
uint32_t entryPointId,
|
||||
uint32_t x,
|
||||
@ -154,6 +158,10 @@ namespace dxvk {
|
||||
uint32_t specConstBool(
|
||||
bool v);
|
||||
|
||||
uint32_t specConst32(
|
||||
uint32_t typeId,
|
||||
uint32_t value);
|
||||
|
||||
void decorate(
|
||||
uint32_t object,
|
||||
spv::Decoration decoration);
|
||||
|
@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <x86intrin.h>
|
||||
#else
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace dxvk::bit {
|
||||
|
||||
@ -17,7 +21,9 @@ namespace dxvk::bit {
|
||||
}
|
||||
|
||||
inline uint32_t tzcnt(uint32_t n) {
|
||||
#if defined(__BMI__)
|
||||
#if defined(_MSC_VER)
|
||||
return _tzcnt_u32(n);
|
||||
#elif defined(__BMI__)
|
||||
return __tzcnt_u32(n);
|
||||
#elif defined(__GNUC__)
|
||||
uint32_t res;
|
||||
|
Loading…
Reference in New Issue
Block a user