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:
Philip Rebohle 2018-05-30 16:58:46 +02:00
commit 7fe4a70342
59 changed files with 2432 additions and 563 deletions

View File

@ -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:

View File

@ -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 },

View File

@ -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);
});
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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(

View File

@ -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;
}
}

View File

@ -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:

View File

@ -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) },
}};

View File

@ -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>;

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -37,6 +37,9 @@ namespace dxvk {
DxbcClipCullInfo clipCullIn;
DxbcClipCullInfo clipCullOut;
bool usesDerivatives = false;
bool usesKill = false;
};
/**

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
};

View File

@ -999,7 +999,9 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* DclGsInstanceCount */
{ },
{ 1, DxbcInstClass::Declaration, {
{ DxbcOperandKind::Imm32, DxbcScalarType::Uint32 },
} },
}};

View File

@ -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();

View File

@ -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;
}

View File

@ -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>;

View File

@ -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;
}

View File

@ -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(

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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:

View File

@ -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;
/**

View File

@ -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
View 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
View 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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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',

View File

@ -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;

View File

@ -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, &region, 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

View File

@ -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();

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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);
};

View 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
View 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;
};
}

View File

@ -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"));

View File

@ -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
*

View 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;
}
}

View 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;
}

View File

@ -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',

View 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);
}

View 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);
}

View 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)));
}

View 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();
}

View 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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;