From a6ace7908f7a8bf9e7c8fe81f38a18efeed8ccbb Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 18 May 2018 22:26:16 +0200 Subject: [PATCH 01/46] [dxbc] Do not emit empty 'else' blocks --- src/dxbc/dxbc_compiler.cpp | 53 +++++++++++++++++++------------------- src/dxbc/dxbc_compiler.h | 3 ++- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 57bd3d2a..0a64439b 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -3258,29 +3258,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 +3280,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 +3301,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); } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 9b7dd1f7..d8d1b816 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -244,10 +244,11 @@ namespace dxvk { struct DxbcCfgBlockIf { + uint32_t ztestId; uint32_t labelIf; uint32_t labelElse; uint32_t labelEnd; - bool hadElse; + size_t headerPtr; }; From 126c50a6743835c669a3dbf95daf19760ff6651b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 19 May 2018 09:07:31 +0200 Subject: [PATCH 02/46] [dxbc] SampleMask does not depend on SampleRateShading This was fixed in a later revision of the SPIR-V 1.0 specification. --- src/dxbc/dxbc_compiler.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 0a64439b..b20a0d35 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -456,7 +456,6 @@ namespace dxvk { } break; case DxbcOperandType::InputCoverageMask: { - m_module.enableCapability(spv::CapabilitySampleRateShading); m_ps.builtinSampleMaskIn = emitNewBuiltinVariable({ { DxbcScalarType::Uint32, 1, 1 }, spv::StorageClassInput }, @@ -465,7 +464,6 @@ namespace dxvk { } break; case DxbcOperandType::OutputCoverageMask: { - m_module.enableCapability(spv::CapabilitySampleRateShading); m_ps.builtinSampleMaskOut = emitNewBuiltinVariable({ { DxbcScalarType::Uint32, 1, 1 }, spv::StorageClassOutput }, From 5cc3afcf302029dd9d2b73a1efa5acca7002bf2f Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 19 May 2018 08:26:25 +0100 Subject: [PATCH 03/46] Fix tzcnt intrinsic on MSVC (#381) --- src/util/util_bit.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/util_bit.h b/src/util/util_bit.h index 75e23f9e..e600fb20 100644 --- a/src/util/util_bit.h +++ b/src/util/util_bit.h @@ -1,6 +1,10 @@ #pragma once +#ifndef _MSC_VER #include +#else +#include +#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; From 425a5bca2eb99fede16f16ec163c8efede60c66e Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 00:11:32 +0200 Subject: [PATCH 04/46] [d3d11] Optimized buffer mapping on deferred contexts --- src/d3d11/d3d11_context_def.cpp | 53 +++++++++++++++++++++++++-------- src/d3d11/d3d11_context_def.h | 14 +++++---- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/d3d11/d3d11_context_def.cpp b/src/d3d11/d3d11_context_def.cpp index 2aecbcb6..5acb5db2 100644 --- a/src/d3d11/d3d11_context_def.cpp +++ b/src/d3d11/d3d11_context_def.cpp @@ -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(pResource); + D3D11Buffer* pBuffer = static_cast(pResource); const Rc 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(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); + }); + } } diff --git a/src/d3d11/d3d11_context_def.h b/src/d3d11/d3d11_context_def.h index 9821b045..bf2fc6d5 100644 --- a/src/d3d11/d3d11_context_def.h +++ b/src/d3d11/d3d11_context_def.h @@ -11,12 +11,14 @@ namespace dxvk { struct D3D11DeferredContextMapEntry { - Com pResource; - UINT Subresource; - D3D11_MAP MapType; - UINT RowPitch; - UINT DepthPitch; - DxvkDataSlice DataSlice; + Com pResource; + UINT Subresource; + D3D11_MAP MapType; + UINT RowPitch; + UINT DepthPitch; + DxvkDataSlice DataSlice; + DxvkPhysicalBufferSlice BufferSlice; + void* MapPointer; }; class D3D11DeferredContext : public D3D11DeviceContext { From 7f619d90519a996243430fc3b556687ae2bab6df Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 00:32:44 +0200 Subject: [PATCH 05/46] [dxvk] Use only one extra thread for async pipeline compilation With the benefits of asynchronous compilation being generally low and compilation in advance not being feasible, there is no reason to create more threads. --- src/dxvk/dxvk_pipecompiler.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dxvk/dxvk_pipecompiler.cpp b/src/dxvk/dxvk_pipecompiler.cpp index 425fe723..6d41a59d 100644 --- a/src/dxvk/dxvk_pipecompiler.cpp +++ b/src/dxvk/dxvk_pipecompiler.cpp @@ -4,9 +4,7 @@ namespace dxvk { DxvkPipelineCompiler::DxvkPipelineCompiler() { - // Use ~half the CPU cores for pipeline compilation - const uint32_t threadCount = std::max( - 1u, std::thread::hardware_concurrency() / 2); + constexpr uint32_t threadCount = 1u; Logger::debug(str::format( "DxvkPipelineCompiler: Using ", threadCount, " workers")); From fb11acbc9139ea2567a4b60ff1ea5ff9330c9e8c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 19:36:53 +0200 Subject: [PATCH 06/46] [dxbc] Implement geometry shader instancing Required for Frostpunk (see #385). --- src/dxbc/dxbc_compiler.cpp | 23 +++++++++++++++++++++++ src/dxbc/dxbc_compiler.h | 4 ++++ src/dxbc/dxbc_defs.cpp | 4 +++- src/spirv/spirv_module.cpp | 10 ++++++++++ src/spirv/spirv_module.h | 4 ++++ 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index b20a0d35..73c7bba1 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -277,6 +277,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: ", @@ -545,6 +548,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: ", @@ -1219,6 +1230,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) { @@ -4282,6 +4300,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( diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index d8d1b816..7f82498a 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -120,6 +120,7 @@ namespace dxvk { uint32_t builtinLayer = 0; uint32_t builtinViewportId = 0; + uint32_t builtinInvocationId = 0; }; @@ -514,6 +515,9 @@ namespace dxvk { void emitDclThreadGroup( const DxbcShaderInstruction& ins); + void emitDclGsInstanceCount( + const DxbcShaderInstruction& ins); + uint32_t emitDclUavCounter( uint32_t regId); diff --git a/src/dxbc/dxbc_defs.cpp b/src/dxbc/dxbc_defs.cpp index 499fe669..0579ef7f 100644 --- a/src/dxbc/dxbc_defs.cpp +++ b/src/dxbc/dxbc_defs.cpp @@ -999,7 +999,9 @@ namespace dxvk { { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, } }, /* DclGsInstanceCount */ - { }, + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, }}; diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index caf90fef..87d211df 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -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, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 93da6952..febd46ca 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -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, From 51104c104d42bf5dd0916841a36fb4edcb896159 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 21:06:26 +0200 Subject: [PATCH 07/46] [d3d11] Refactor InitTexture method --- src/d3d11/d3d11_device.cpp | 12 ++++++------ src/d3d11/d3d11_device.h | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index be397366..9eb1ecae 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -182,7 +182,7 @@ namespace dxvk { try { const Com 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 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 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) { @@ -1849,8 +1849,7 @@ namespace dxvk { void D3D11Device::InitBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData) { - const DxvkBufferSlice bufferSlice - = pBuffer->GetBufferSlice(); + const DxvkBufferSlice bufferSlice = pBuffer->GetBufferSlice(); if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) { LockResourceInitContext(); @@ -1867,8 +1866,9 @@ namespace dxvk { void D3D11Device::InitTexture( - const Rc& image, + D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData) { + const Rc image = pTexture->GetImage(); const DxvkFormatInfo* formatInfo = imageFormatInfo(image->info().format); if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) { diff --git a/src/d3d11/d3d11_device.h b/src/d3d11/d3d11_device.h index abc09a2c..1f52456e 100644 --- a/src/d3d11/d3d11_device.h +++ b/src/d3d11/d3d11_device.h @@ -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& image, + D3D11CommonTexture* pTexture, const D3D11_SUBRESOURCE_DATA* pInitialData); HRESULT GetFormatSupportFlags( From a39b9cb131f453d34f8498090413157565377931 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 21:10:39 +0200 Subject: [PATCH 08/46] [d3d11] Pre-clear buffers with D3D11_USAGE_DEFAULT Some games may expect buffers, like images, to be pre-initialized. --- src/d3d11/d3d11_device.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index 9eb1ecae..f58cc02c 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -1851,6 +1851,9 @@ namespace dxvk { const D3D11_SUBRESOURCE_DATA* pInitialData) { const DxvkBufferSlice bufferSlice = pBuffer->GetBufferSlice(); + D3D11_BUFFER_DESC desc; + pBuffer->GetDesc(&desc); + if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) { LockResourceInitContext(); @@ -1860,6 +1863,16 @@ 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); } } From 979ba2d7c659aff0e7f6bf46adf193054ababe3b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 23:48:07 +0200 Subject: [PATCH 09/46] [dxgi] Implemented IDXGIAdapter2 --- src/dxgi/dxgi_adapter.cpp | 71 ++++++++++++++++++++++++++------------ src/dxgi/dxgi_adapter.h | 3 ++ src/dxgi/dxgi_interfaces.h | 2 +- 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index 8bf91fc3..c88317bf 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -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(); @@ -163,15 +186,17 @@ namespace dxvk { 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; } diff --git a/src/dxgi/dxgi_adapter.h b/src/dxgi/dxgi_adapter.h index 3bdd0bcb..02014fb0 100644 --- a/src/dxgi/dxgi_adapter.h +++ b/src/dxgi/dxgi_adapter.h @@ -43,6 +43,9 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE GetDesc1( DXGI_ADAPTER_DESC1* pDesc) final; + HRESULT STDMETHODCALLTYPE GetDesc2( + DXGI_ADAPTER_DESC2* pDesc) final; + Rc STDMETHODCALLTYPE GetDXVKAdapter() final; HRESULT STDMETHODCALLTYPE CreateDevice( diff --git a/src/dxgi/dxgi_interfaces.h b/src/dxgi/dxgi_interfaces.h index 7f76f257..82119529 100644 --- a/src/dxgi/dxgi_interfaces.h +++ b/src/dxgi/dxgi_interfaces.h @@ -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 STDMETHODCALLTYPE GetDXVKAdapter() = 0; From 5a61d81135e58391a96ec2e67776bf69cdf1a4da Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 23:50:28 +0200 Subject: [PATCH 10/46] [dxgi] Stubbed out IDXGISwapChain1 --- src/dxgi/dxgi_swapchain.cpp | 86 +++++++++++++++++++++++++++++++- src/dxgi/dxgi_swapchain.h | 98 +++++++++++++++++++++++++------------ 2 files changed, 152 insertions(+), 32 deletions(-) diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 0fcded51..28a145f8 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -73,7 +73,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; } @@ -142,6 +143,35 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) { + Logger::err("DxgiSwapChain::GetDesc1: Not implemented"); + return E_NOTIMPL; + } + + + 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 lock(m_mutex); @@ -177,6 +207,28 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc( + DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) { + Logger::err("DxgiSwapChain::GetFullscreenDesc: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetHwnd( + HWND* pHwnd) { + Logger::err("DxgiSwapChain::GetHwnd: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetCoreWindow( + REFIID refiid, + void** ppUnk) { + Logger::err("DxgiSwapChain::GetCoreWindow: Not implemented"); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) { std::lock_guard lock(m_mutex); @@ -188,6 +240,13 @@ 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 lock(m_mutex); @@ -235,6 +294,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, @@ -335,6 +405,20 @@ namespace dxvk { } + 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 lock(m_mutex); diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index b6c75c84..4b694bbe 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -20,69 +20,105 @@ namespace dxvk { class DxgiFactory; class DxgiOutput; - class DxgiSwapChain : public DxgiObject { + class DxgiSwapChain : public DxgiObject { public: DxgiSwapChain( - DxgiFactory* factory, - IUnknown* pDevice, - DXGI_SWAP_CHAIN_DESC* pDesc); + DxgiFactory* factory, + IUnknown* pDevice, + DXGI_SWAP_CHAIN_DESC* pDesc); + ~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(); From 56e7389495668ec60656ade9c5ffb5ce23b7c22c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 23:52:03 +0200 Subject: [PATCH 11/46] [dxgi] Stubbed out IDXGIFactory2 --- src/dxgi/dxgi_factory.cpp | 102 +++++++++++++++++++++++++++++++++++++- src/dxgi/dxgi_factory.h | 55 +++++++++++++++++++- 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index d1638d6d..5b9e55d5 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -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) { @@ -76,6 +83,45 @@ namespace dxvk { } + 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); + + Logger::err("DxgiFactory::CreateSwapChainForHwnd: Not implemented"); + return E_NOTIMPL; + } + + + 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 +163,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 +183,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"); + } + } diff --git a/src/dxgi/dxgi_factory.h b/src/dxgi/dxgi_factory.h index 91859122..524aacda 100644 --- a/src/dxgi/dxgi_factory.h +++ b/src/dxgi/dxgi_factory.h @@ -8,7 +8,7 @@ namespace dxvk { - class DxgiFactory : public DxgiObject { + class DxgiFactory : public DxgiObject { 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: From 58fa8159265a9e918d4f684e499357c5b18ca78a Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 22 May 2018 23:52:12 +0200 Subject: [PATCH 12/46] [dxgi] Fixed error message formatting in DxgiDevice --- src/dxgi/dxgi_device.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dxgi/dxgi_device.cpp b/src/dxgi/dxgi_device.cpp index 1ca477d5..28a0ba15 100644 --- a/src/dxgi/dxgi_device.cpp +++ b/src/dxgi/dxgi_device.cpp @@ -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; } From d1b705bf0d6699082a5a8a2850a8fbcb7591fd06 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 23 May 2018 01:06:34 +0200 Subject: [PATCH 13/46] [dxgi] Implemented IDXGISwapChain1 --- src/d3d11/d3d11_present.cpp | 8 +- src/d3d11/d3d11_present.h | 2 +- src/dxgi/dxgi_factory.cpp | 65 ++++++++++++---- src/dxgi/dxgi_interfaces.h | 2 +- src/dxgi/dxgi_swapchain.cpp | 150 ++++++++++++++++++++++-------------- src/dxgi/dxgi_swapchain.h | 12 ++- 6 files changed, 157 insertions(+), 82 deletions(-) diff --git a/src/d3d11/d3d11_present.cpp b/src/d3d11/d3d11_present.cpp index dc7135da..9c1a56d2 100644 --- a/src/d3d11/d3d11_present.cpp +++ b/src/d3d11/d3d11_present.cpp @@ -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 diff --git a/src/d3d11/d3d11_present.h b/src/d3d11/d3d11_present.h index 1fe147d6..9c7063f1 100644 --- a/src/d3d11/d3d11_present.h +++ b/src/d3d11/d3d11_present.h @@ -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(); diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index 5b9e55d5..dcffe6f9 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -65,21 +65,36 @@ namespace dxvk { IUnknown* pDevice, DXGI_SWAP_CHAIN_DESC* pDesc, IDXGISwapChain** ppSwapChain) { - InitReturnPtr(ppSwapChain); - - if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == NULL) + if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == nullptr) return DXGI_ERROR_INVALID_CALL; - if (pDesc->OutputWindow == 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; - try { - *ppSwapChain = ref(new DxgiSwapChain(this, pDevice, pDesc)); - return S_OK; - } catch (const DxvkError& e) { - Logger::err(e.message()); - return E_FAIL; - } + 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; } @@ -92,8 +107,30 @@ namespace dxvk { IDXGISwapChain1** ppSwapChain) { InitReturnPtr(ppSwapChain); - Logger::err("DxgiFactory::CreateSwapChainForHwnd: Not implemented"); - return E_NOTIMPL; + if (ppSwapChain == nullptr || pDesc == nullptr || hWnd == nullptr || pDevice == 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, hWnd, pDesc, &fullscreenDesc)); + return S_OK; + } catch (const DxvkError& e) { + Logger::err(e.message()); + return E_FAIL; + } } diff --git a/src/dxgi/dxgi_interfaces.h b/src/dxgi/dxgi_interfaces.h index 82119529..d46f98d9 100644 --- a/src/dxgi/dxgi_interfaces.h +++ b/src/dxgi/dxgi_interfaces.h @@ -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; /** diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 28a145f8..ceca91b0 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -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())) @@ -100,7 +103,7 @@ namespace dxvk { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; if (Buffer > 0) { @@ -117,11 +120,11 @@ namespace dxvk { std::lock_guard 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, @@ -138,14 +141,31 @@ namespace dxvk { if (pDesc == nullptr) return DXGI_ERROR_INVALID_CALL; - *pDesc = m_desc; + 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) { - Logger::err("DxgiSwapChain::GetDesc1: Not implemented"); - return E_NOTIMPL; + std::lock_guard lock(m_mutex); + + if (pDesc == nullptr) + return DXGI_ERROR_INVALID_CALL; + + *pDesc = m_desc; + return S_OK; } @@ -188,18 +208,18 @@ namespace dxvk { IDXGIOutput** ppTarget) { std::lock_guard 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); } @@ -209,21 +229,31 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc( DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) { - Logger::err("DxgiSwapChain::GetFullscreenDesc: Not implemented"); - return E_NOTIMPL; + std::lock_guard lock(m_mutex); + + if (pDesc == nullptr) + return DXGI_ERROR_INVALID_CALL; + + *pDesc = m_descFs; + return S_OK; } HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetHwnd( HWND* pHwnd) { - Logger::err("DxgiSwapChain::GetHwnd: Not implemented"); - return E_NOTIMPL; + 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; } @@ -250,7 +280,7 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; if (Flags & DXGI_PRESENT_TEST) @@ -278,7 +308,7 @@ namespace dxvk { // Vulkan swap chain itself remains valid. DxvkSwapchainProperties swapchainProps; swapchainProps.preferredSurfaceFormat - = m_presenter->PickSurfaceFormat(m_desc.BufferDesc.Format); + = m_presenter->PickSurfaceFormat(m_desc.Format); swapchainProps.preferredPresentMode = SyncInterval == 0 ? m_presenter->PickPresentMode(VK_PRESENT_MODE_IMMEDIATE_KHR) : m_presenter->PickPresentMode(VK_PRESENT_MODE_FIFO_KHR); @@ -313,19 +343,19 @@ namespace dxvk { UINT SwapChainFlags) { std::lock_guard 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(); } @@ -337,29 +367,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 output; @@ -379,7 +409,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); } @@ -393,12 +423,12 @@ namespace dxvk { IDXGIOutput* pTarget) { std::lock_guard 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; @@ -457,7 +487,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()); @@ -496,7 +526,7 @@ namespace dxvk { VkExtent2D DxgiSwapChain::GetWindowSize() const { RECT windowRect; - if (!::GetClientRect(m_desc.OutputWindow, &windowRect)) + if (!::GetClientRect(m_window, &windowRect)) windowRect = RECT(); VkExtent2D result; @@ -517,14 +547,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"); @@ -533,11 +567,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; @@ -545,8 +579,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; @@ -554,7 +588,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); @@ -571,24 +605,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); diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index 4b694bbe..188c1c88 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -25,9 +25,11 @@ namespace dxvk { 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(); @@ -137,7 +139,9 @@ namespace dxvk { Com m_device; Com 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 m_presenter; From 531732fe91fe59ed94447eac1e922be45fbf0bf9 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 23 May 2018 01:32:52 +0200 Subject: [PATCH 14/46] [dxgi] Add IDXGIFactory2 to supported interfaces --- src/dxgi/dxgi_main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_main.cpp b/src/dxgi/dxgi_main.cpp index b602e721..dc724737 100644 --- a/src/dxgi/dxgi_main.cpp +++ b/src/dxgi/dxgi_main.cpp @@ -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; From 38c5e570257e8cd9dcb78ab227baa2a2c877bd0f Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 23 May 2018 13:03:12 +0200 Subject: [PATCH 15/46] [dxgi] Refactor Vulkan swap chain and surface creation Creating the Vulkan surface at the latest possible moment fixes an issue with Frostpunk, which renders to a D3D9 swap chain before presenting to the GXGI swap chain. --- src/dxgi/dxgi_presenter.cpp | 75 +++++++++++++++++++++---------------- src/dxgi/dxgi_presenter.h | 34 +++++++---------- src/dxgi/dxgi_swapchain.cpp | 14 +++---- 3 files changed, 62 insertions(+), 61 deletions(-) diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index 116ea212..431cca37 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -10,15 +10,10 @@ namespace dxvk { DxgiVkPresenter::DxgiVkPresenter( const Rc& 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( - GetWindowLongPtr(window, GWLP_HINSTANCE)); - - m_surface = m_device->adapter()->createSurface(instance, window); - // Reset options for the swap chain itself. We will // create a swap chain object before presentation. m_options.preferredSurfaceFormat = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; @@ -275,27 +270,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 +359,11 @@ namespace dxvk { } - void DxgiVkPresenter::SetGammaControl( - const DXGI_VK_GAMMA_CURVE* pGammaCurve) { - m_context->beginRecording( - m_device->createCommandList()); + Rc DxgiVkPresenter::CreateSurface() { + HINSTANCE instance = reinterpret_cast( + 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); } diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h index 1ce32606..8709849c 100644 --- a/src/dxgi/dxgi_presenter.h +++ b/src/dxgi/dxgi_presenter.h @@ -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 m_device; Rc 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 CreateSurface(); + Rc CreateSampler( VkFilter Filter, VkSamplerAddressMode AddressMode); diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index ceca91b0..343cbc04 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -306,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.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) { From 61049c33fb7dd54d38f99c9aae0b95953c7a509d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 10:48:06 +0200 Subject: [PATCH 16/46] [dxgi] Reduce reported VRAM on 32-bit platforms This is closer to what Windows does, and some applications may have trouble with more than 3GB VRAM. --- src/dxgi/dxgi_adapter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index c88317bf..8c43a2bf 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -181,7 +181,7 @@ 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 From d9772b0ffd6fda8dfa05ec7df7cf89849ab8cfdc Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 11:44:04 +0200 Subject: [PATCH 17/46] [dxvk] Create image views for all supported view types Rather than creating just one image view per DxvkImageView, we create views for all compatible types in an attempt to work around game bugs in Diablo 3, Far Cry 5, Nier Automata, Dishonored 2, Trackmania etc., which bind incompatible resource views to some resource slots. --- src/dxvk/dxvk_context.cpp | 4 +- src/dxvk/dxvk_image.cpp | 96 ++++++++++++++++++++++++++++----------- src/dxvk/dxvk_image.h | 31 +++++++++---- 3 files changed, 95 insertions(+), 36 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index bb966228..afad9756 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -1822,11 +1822,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 diff --git a/src/dxvk/dxvk_image.cpp b/src/dxvk/dxvk_image.cpp index 3f9937dc..3227c9da 100644 --- a/src/dxvk/dxvk_image.cpp +++ b/src/dxvk/dxvk_image.cpp @@ -87,25 +87,75 @@ namespace dxvk { const Rc& 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); - } - } \ No newline at end of file diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h index 9802d81e..395213bc 100644 --- a/src/dxvk/dxvk_image.h +++ b/src/dxvk/dxvk_image.h @@ -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 { @@ -336,7 +349,9 @@ namespace dxvk { Rc m_image; DxvkImageViewCreateInfo m_info; - VkImageView m_view; + VkImageView m_views[ViewCount]; + + void createView(VkImageViewType type, uint32_t numLayers); }; From 58e9280891b8a07a66bef2243b69bab6e9e4930f Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 11:49:12 +0200 Subject: [PATCH 18/46] [dxbc] Remove Tex2D -> Tex2DArray workaround --- src/d3d11/d3d11_device.cpp | 9 --------- src/dxbc/dxbc_compiler.cpp | 5 ----- src/dxbc/dxbc_options.cpp | 3 +-- src/dxbc/dxbc_options.h | 5 ----- 4 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index f58cc02c..b576143e 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -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: diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 73c7bba1..f3c3b8bf 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -6290,11 +6290,6 @@ namespace dxvk { } }(); - if (typeInfo.dim == spv::Dim2D && m_options.test(DxbcOption::ForceTex2DArray)) { - typeInfo.array = 1; - typeInfo.vtype = VK_IMAGE_VIEW_TYPE_2D_ARRAY; - } - return typeInfo; } diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index 22b925b7..6ccfd565 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -5,8 +5,7 @@ namespace dxvk { const static std::unordered_map g_dxbcAppOptions = {{ - { "Dishonored2.exe", DxbcOptions(DxbcOption::ForceTex2DArray) }, - { "ManiaPlanet.exe", DxbcOptions(DxbcOption::ForceTex2DArray) }, + }}; diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index abe29521..1a8e575c 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -17,11 +17,6 @@ namespace dxvk { /// Use FMin/FMax/FClamp instead of NMin/NMax/NClamp. /// 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, }; using DxbcOptions = Flags; From 12d79257be273d42e853329ed3f10d9f2f013fee Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 12:00:31 +0200 Subject: [PATCH 19/46] [dxbc] Remove TexCube -> TexCubeArray workaround --- src/dxbc/dxbc_compiler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index f3c3b8bf..fe28f6fe 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -828,7 +828,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; @@ -6284,7 +6283,7 @@ namespace dxvk { 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::TextureCube: return { spv::DimCube, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_CUBE }; case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY }; default: throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType)); } From a43025294aae1ae166b6d17efe419ffd7d5930a0 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 12:07:03 +0200 Subject: [PATCH 20/46] [dxbc] Remove DxbcImageInfo::layered property --- src/dxbc/dxbc_compiler.cpp | 47 +++++++++----------------------------- src/dxbc/dxbc_decoder.h | 1 - 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index fe28f6fe..8d918b24 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -963,7 +963,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, @@ -4589,15 +4589,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; } @@ -4642,22 +4633,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; } @@ -6275,16 +6250,16 @@ 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, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_CUBE }; - 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)); } }(); diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index e0b15e4d..df21fcbc 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -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; }; From 66db3e8714443de539058cd8cea6402a1d856fc0 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 12:31:04 +0200 Subject: [PATCH 21/46] [dxgi] Add app-specific DXGI options --- src/dxgi/dxgi_options.cpp | 20 ++++++++++++++++++++ src/dxgi/dxgi_options.h | 30 ++++++++++++++++++++++++++++++ src/dxgi/meson.build | 1 + 3 files changed, 51 insertions(+) create mode 100644 src/dxgi/dxgi_options.cpp create mode 100644 src/dxgi/dxgi_options.h diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp new file mode 100644 index 00000000..94df5f80 --- /dev/null +++ b/src/dxgi/dxgi_options.cpp @@ -0,0 +1,20 @@ +#include "dxgi_options.h" + +#include + +namespace dxvk { + + const static std::unordered_map 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(); + } + +} \ No newline at end of file diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h new file mode 100644 index 00000000..11c8c999 --- /dev/null +++ b/src/dxgi/dxgi_options.h @@ -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; + + /** + * \brief Gets app-specific DXGI options + * + * \param [in] appName Application name + * \returns DXGI options for this application + */ + DxgiOptions getDxgiAppOptions(const std::string& appName); + +} diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index cc76baeb..5dfc3347 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -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', From 23ba3e228afbf6bce56fb8194c9572c37abb234d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 12:31:21 +0200 Subject: [PATCH 22/46] [dxgi] Add option for deferred surface creation Deferred surface creation is required for Frostpunk due to conflicts with the D3D9 swap chain created by the game before it presents the first frame to the DXGI swap chain, but breaks NieR:Automata due to threading issues. --- src/dxgi/dxgi_presenter.cpp | 7 +++++++ src/dxgi/dxgi_presenter.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index 431cca37..f041ab6a 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -14,6 +14,13 @@ namespace dxvk { m_device (device), m_context (device->createContext()) { + // Some games don't work with deferred surface creation, + // so we should default to initializing it immediately. + DxgiOptions dxgiOptions = getDxgiAppOptions(env::getExeName()); + + if (!dxgiOptions.test(DxgiOption::DeferSurfaceCreation)) + m_surface = CreateSurface(); + // Reset options for the swap chain itself. We will // create a swap chain object before presentation. m_options.preferredSurfaceFormat = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h index 8709849c..a497ab34 100644 --- a/src/dxgi/dxgi_presenter.h +++ b/src/dxgi/dxgi_presenter.h @@ -8,7 +8,7 @@ #include "../spirv/spirv_module.h" -#include "dxgi_include.h" +#include "dxgi_options.h" namespace dxvk { From f087016e77a5fe574905b625f344b62eac070c96 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 12:31:04 +0200 Subject: [PATCH 23/46] [dxgi] Add app-specific DXGI options --- src/dxgi/dxgi_options.cpp | 20 ++++++++++++++++++++ src/dxgi/dxgi_options.h | 30 ++++++++++++++++++++++++++++++ src/dxgi/meson.build | 1 + 3 files changed, 51 insertions(+) create mode 100644 src/dxgi/dxgi_options.cpp create mode 100644 src/dxgi/dxgi_options.h diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp new file mode 100644 index 00000000..94df5f80 --- /dev/null +++ b/src/dxgi/dxgi_options.cpp @@ -0,0 +1,20 @@ +#include "dxgi_options.h" + +#include + +namespace dxvk { + + const static std::unordered_map 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(); + } + +} \ No newline at end of file diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h new file mode 100644 index 00000000..11c8c999 --- /dev/null +++ b/src/dxgi/dxgi_options.h @@ -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; + + /** + * \brief Gets app-specific DXGI options + * + * \param [in] appName Application name + * \returns DXGI options for this application + */ + DxgiOptions getDxgiAppOptions(const std::string& appName); + +} diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index cc76baeb..5dfc3347 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -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', From d844ddfdfa41872e77516746cb1e8cd2dfacb669 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 12:31:21 +0200 Subject: [PATCH 24/46] [dxgi] Add option for deferred surface creation Deferred surface creation is required for Frostpunk due to conflicts with the D3D9 swap chain created by the game before it presents the first frame to the DXGI swap chain, but breaks NieR:Automata due to threading issues. --- src/dxgi/dxgi_presenter.cpp | 7 +++++++ src/dxgi/dxgi_presenter.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index 431cca37..f041ab6a 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -14,6 +14,13 @@ namespace dxvk { m_device (device), m_context (device->createContext()) { + // Some games don't work with deferred surface creation, + // so we should default to initializing it immediately. + DxgiOptions dxgiOptions = getDxgiAppOptions(env::getExeName()); + + if (!dxgiOptions.test(DxgiOption::DeferSurfaceCreation)) + m_surface = CreateSurface(); + // Reset options for the swap chain itself. We will // create a swap chain object before presentation. m_options.preferredSurfaceFormat = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h index 8709849c..a497ab34 100644 --- a/src/dxgi/dxgi_presenter.h +++ b/src/dxgi/dxgi_presenter.h @@ -8,7 +8,7 @@ #include "../spirv/spirv_module.h" -#include "dxgi_include.h" +#include "dxgi_options.h" namespace dxvk { From 254676049ad9aba75d15621ddde8a6995ebbb154 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 20:18:37 +0200 Subject: [PATCH 25/46] [dxvk] Added convenience equal checker for unordered maps --- src/dxvk/dxvk_hash.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dxvk/dxvk_hash.h b/src/dxvk/dxvk_hash.h index 0ee55dcb..a0fe561d 100644 --- a/src/dxvk/dxvk_hash.h +++ b/src/dxvk/dxvk_hash.h @@ -4,6 +4,13 @@ namespace dxvk { + struct DxvkEq { + template + size_t operator () (const T& a, const T& b) const { + return a.eq(b); + } + }; + struct DxvkHash { template size_t operator () (const T& object) const { From a9c0f9e7c28f86201059cb237497ae21d823ce2e Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 24 May 2018 23:08:16 +0200 Subject: [PATCH 26/46] [general] Added DXUP reference to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f80a1d89..6523a244 100644 --- a/README.md +++ b/README.md @@ -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: From 49bda46a3706e9c6f6ef2ef9113c68adbd5f57d7 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 00:08:28 +0200 Subject: [PATCH 27/46] [d3d11] Validate and correct scissor rects Fixes Vulkan validation errors in Frostpunk and more closely emulates Windows behaviour. --- src/d3d11/d3d11_context.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 9e922901..d42bde92 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -2290,8 +2290,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 +2557,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(sr.right - sr.left), - static_cast(sr.bottom - sr.top) } }; + VkOffset2D srPosA; + srPosA.x = std::max(0, sr.left); + srPosA.y = std::max(0, sr.top); + + VkOffset2D srPosB; + srPosB.x = std::max(srPosA.x, sr.right); + srPosB.y = std::max(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 }, From 7ec93debf10cca759067a6a137f9730f0ca5adad Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 17:44:34 +0200 Subject: [PATCH 28/46] [dxvk] Add shaders for mip map generation --- src/dxvk/shaders/dxvk_mipgen_frag_1d.frag | 11 ++++++++++ src/dxvk/shaders/dxvk_mipgen_frag_2d.frag | 11 ++++++++++ src/dxvk/shaders/dxvk_mipgen_frag_3d.frag | 17 +++++++++++++++ src/dxvk/shaders/dxvk_mipgen_geom.geom | 25 +++++++++++++++++++++++ src/dxvk/shaders/dxvk_mipgen_vert.vert | 8 ++++++++ 5 files changed, 72 insertions(+) create mode 100644 src/dxvk/shaders/dxvk_mipgen_frag_1d.frag create mode 100644 src/dxvk/shaders/dxvk_mipgen_frag_2d.frag create mode 100644 src/dxvk/shaders/dxvk_mipgen_frag_3d.frag create mode 100644 src/dxvk/shaders/dxvk_mipgen_geom.geom create mode 100644 src/dxvk/shaders/dxvk_mipgen_vert.vert diff --git a/src/dxvk/shaders/dxvk_mipgen_frag_1d.frag b/src/dxvk/shaders/dxvk_mipgen_frag_1d.frag new file mode 100644 index 00000000..0e181951 --- /dev/null +++ b/src/dxvk/shaders/dxvk_mipgen_frag_1d.frag @@ -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); +} \ No newline at end of file diff --git a/src/dxvk/shaders/dxvk_mipgen_frag_2d.frag b/src/dxvk/shaders/dxvk_mipgen_frag_2d.frag new file mode 100644 index 00000000..e7a0b082 --- /dev/null +++ b/src/dxvk/shaders/dxvk_mipgen_frag_2d.frag @@ -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); +} \ No newline at end of file diff --git a/src/dxvk/shaders/dxvk_mipgen_frag_3d.frag b/src/dxvk/shaders/dxvk_mipgen_frag_3d.frag new file mode 100644 index 00000000..42429cd9 --- /dev/null +++ b/src/dxvk/shaders/dxvk_mipgen_frag_3d.frag @@ -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))); +} \ No newline at end of file diff --git a/src/dxvk/shaders/dxvk_mipgen_geom.geom b/src/dxvk/shaders/dxvk_mipgen_geom.geom new file mode 100644 index 00000000..c7413a43 --- /dev/null +++ b/src/dxvk/shaders/dxvk_mipgen_geom.geom @@ -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(); +} \ No newline at end of file diff --git a/src/dxvk/shaders/dxvk_mipgen_vert.vert b/src/dxvk/shaders/dxvk_mipgen_vert.vert new file mode 100644 index 00000000..3009b809 --- /dev/null +++ b/src/dxvk/shaders/dxvk_mipgen_vert.vert @@ -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); +} \ No newline at end of file From 19b6a1617338ba094fa1461c32400b2a372520e4 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 17:44:50 +0200 Subject: [PATCH 29/46] [dxvk] Add new mip map generator The current approach uses Vulkan blits, which does not work if the image view in question has a different format than the image itself. --- src/dxvk/dxvk_image.h | 12 +- src/dxvk/dxvk_meta_mipgen.cpp | 554 ++++++++++++++++++++++++++++++++++ src/dxvk/dxvk_meta_mipgen.h | 232 ++++++++++++++ src/dxvk/meson.build | 7 + 4 files changed, 803 insertions(+), 2 deletions(-) create mode 100644 src/dxvk/dxvk_meta_mipgen.cpp create mode 100644 src/dxvk/dxvk_meta_mipgen.h diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h index 395213bc..afbb574e 100644 --- a/src/dxvk/dxvk_image.h +++ b/src/dxvk/dxvk_image.h @@ -285,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 @@ -302,8 +310,8 @@ namespace dxvk { } /** - * \brief Image - * \returns Image + * \brief Image object + * \returns Image object */ Rc image() const { return m_image; diff --git a/src/dxvk/dxvk_meta_mipgen.cpp b/src/dxvk/dxvk_meta_mipgen.cpp new file mode 100644 index 00000000..8d5d55ee --- /dev/null +++ b/src/dxvk/dxvk_meta_mipgen.cpp @@ -0,0 +1,554 @@ +#include "dxvk_meta_mipgen.h" + +#include +#include +#include +#include +#include + +namespace dxvk { + + DxvkMetaMipGenRenderPass::DxvkMetaMipGenRenderPass( + const Rc& vkd, + const Rc& view) + : m_vkd(vkd), m_view(view), m_renderPass(createRenderPass()) { + // Determine view type based on image type + const std::array, 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 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& 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 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 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 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; + } + +} diff --git a/src/dxvk/dxvk_meta_mipgen.h b/src/dxvk/dxvk_meta_mipgen.h new file mode 100644 index 00000000..9f6600ad --- /dev/null +++ b/src/dxvk/dxvk_meta_mipgen.h @@ -0,0 +1,232 @@ +#pragma once + +#include +#include + +#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& vkd, + const Rc& 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 m_vkd; + Rc m_view; + + VkRenderPass m_renderPass; + + VkImageViewType m_srcViewType; + VkImageViewType m_dstViewType; + + std::vector 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& 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 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; + + }; + +} diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 1c563df9..e2a5fabf 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -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_pipecache.cpp', 'dxvk_pipecompiler.cpp', From 4b37590e1419deb3e3ef242e5fbb8d3cfacb3b61 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 17:45:41 +0200 Subject: [PATCH 30/46] [dxvk] Use new mip map generator --- src/d3d11/d3d11_context.cpp | 4 +- src/dxvk/dxvk_context.cpp | 209 +++++++++++++++++------------------- src/dxvk/dxvk_context.h | 17 +-- src/dxvk/dxvk_device.cpp | 26 ++--- src/dxvk/dxvk_device.h | 1 + 5 files changed, 126 insertions(+), 131 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 9e922901..bf330af5 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -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"); diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index afad9756..f440ef64 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -7,12 +7,14 @@ namespace dxvk { DxvkContext::DxvkContext( - const Rc& device, - const Rc& pipelineManager, - const Rc& metaClearObjects) + const Rc& device, + const Rc& pipelineManager, + const Rc& metaClearObjects, + const Rc& metaMipGenObjects) : m_device (device), m_pipeMgr (pipelineManager), - m_metaClear (metaClearObjects) { } + m_metaClear (metaClearObjects), + m_metaMipGen(metaMipGenObjects) { } DxvkContext::~DxvkContext() { @@ -976,123 +978,102 @@ namespace dxvk { void DxvkContext::generateMipmaps( - const Rc& image, - const VkImageSubresourceRange& subresources) { - if (subresources.levelCount <= 1) + const Rc& 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 mipGenerator + = new DxvkMetaMipGenRenderPass(m_device->vkd(), imageView); - m_barriers.recordCommands(m_cmd); + // Common descriptor set properties that we use to + // bind the source image view to the fragment shader + VkDescriptorImageInfo descriptorImage; + descriptorImage.sampler = VK_NULL_HANDLE; + descriptorImage.imageView = VK_NULL_HANDLE; + descriptorImage.imageLayout = imageView->imageInfo().layout; - // Generate each individual mip level with a blit - for (uint32_t i = 1; i < subresources.levelCount; i++) { - const uint32_t mip = subresources.baseMipLevel + i; + VkWriteDescriptorSet descriptorWrite; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.pNext = nullptr; + descriptorWrite.dstSet = VK_NULL_HANDLE; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorCount = 1; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite.pImageInfo = &descriptorImage; + descriptorWrite.pBufferInfo = nullptr; + descriptorWrite.pTexelBufferView = nullptr; + + // Common render pass info + VkRenderPassBeginInfo passInfo; + passInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + passInfo.pNext = nullptr; + passInfo.renderPass = mipGenerator->renderPass(); + passInfo.framebuffer = VK_NULL_HANDLE; + passInfo.renderArea = VkRect2D { }; + passInfo.clearValueCount = 0; + passInfo.pClearValues = nullptr; + + // Retrieve a compatible pipeline to use for rendering + DxvkMetaMipGenPipeline pipeInfo = m_metaMipGen->getPipeline( + mipGenerator->viewType(), imageView->info().format); + + for (uint32_t i = 0; i < mipGenerator->passCount(); i++) { + DxvkMetaMipGenPass pass = mipGenerator->pass(i); - const VkExtent3D srcExtent = image->mipLevelExtent(mip - 1); - const VkExtent3D dstExtent = image->mipLevelExtent(mip); + // Width, height and layer count for the current pass + VkExtent3D passExtent = mipGenerator->passExtent(i); - VkImageBlit region; - region.srcSubresource = VkImageSubresourceLayers { - subresources.aspectMask, mip - 1, - subresources.baseArrayLayer, - subresources.layerCount }; - region.srcOffsets[0] = VkOffset3D { 0, 0, 0 }; - region.srcOffsets[1].x = srcExtent.width; - region.srcOffsets[1].y = srcExtent.height; - region.srcOffsets[1].z = srcExtent.depth; + // Create descriptor set with the current source view + descriptorImage.imageView = pass.srcView; + descriptorWrite.dstSet = m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout); + m_cmd->updateDescriptorSets(1, &descriptorWrite); - region.dstSubresource = VkImageSubresourceLayers { - subresources.aspectMask, mip, - subresources.baseArrayLayer, - subresources.layerCount }; - region.dstOffsets[0] = VkOffset3D { 0, 0, 0 }; - region.dstOffsets[1].x = dstExtent.width; - region.dstOffsets[1].y = dstExtent.height; - region.dstOffsets[1].z = dstExtent.depth; + // Set up viewport and scissor rect + VkViewport viewport; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = float(passExtent.width); + viewport.height = float(passExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; - m_cmd->cmdBlitImage( - image->handle(), image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), - image->handle(), image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), - 1, ®ion, VK_FILTER_LINEAR); + VkRect2D scissor; + scissor.offset = { 0, 0 }; + scissor.extent = { passExtent.width, passExtent.height }; - if (i + 1 < subresources.levelCount) { - m_barriers.accessImage(image, - VkImageSubresourceRange { - subresources.aspectMask, mip, 1, - subresources.baseArrayLayer, - subresources.layerCount }, - image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT); - m_barriers.recordCommands(m_cmd); - } + // Set up render pass info + passInfo.framebuffer = pass.framebuffer; + passInfo.renderArea = scissor; + + // Set up push constants + DxvkMetaMipGenPushConstants pushConstants; + pushConstants.layerCount = passExtent.depth; + + m_cmd->cmdBeginRenderPass(&passInfo, VK_SUBPASS_CONTENTS_INLINE); + m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeInfo.pipeHandle); + m_cmd->cmdBindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeInfo.pipeLayout, descriptorWrite.dstSet); + + m_cmd->cmdSetViewport(0, 1, &viewport); + m_cmd->cmdSetScissor (0, 1, &scissor); + + m_cmd->cmdPushConstants( + pipeInfo.pipeLayout, + VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(pushConstants), + &pushConstants); + + m_cmd->cmdDraw(1, passExtent.depth, 0, 0); + m_cmd->cmdEndRenderPass(); } - // Transform mip levels back into their original layout. - // The last mip level is still in TRANSFER_DST_OPTIMAL. - m_barriers.accessImage(image, - VkImageSubresourceRange { - subresources.aspectMask, - subresources.baseMipLevel, - subresources.levelCount - 1, - subresources.baseArrayLayer, - subresources.layerCount }, - image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - image->info().layout, - image->info().stages, - image->info().access); - - m_barriers.accessImage(image, - VkImageSubresourceRange { - subresources.aspectMask, - subresources.baseMipLevel - + subresources.levelCount - 1, 1, - subresources.baseArrayLayer, - subresources.layerCount }, - image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - image->info().layout, - image->info().stages, - image->info().access); - - m_barriers.recordCommands(m_cmd); + m_cmd->trackResource(mipGenerator); + m_cmd->trackResource(imageView); } @@ -1680,6 +1661,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); diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index fc74bfe2..24f3ae81 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -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& device, - const Rc& pipelineManager, - const Rc& metaClearObjects); + const Rc& device, + const Rc& pipelineManager, + const Rc& metaClearObjects, + const Rc& 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& image, - const VkImageSubresourceRange& subresources); + const Rc& imageView); /** * \brief Initializes or invalidates an image @@ -621,6 +621,7 @@ namespace dxvk { const Rc m_device; const Rc m_pipeMgr; const Rc m_metaClear; + const Rc m_metaMipGen; Rc 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(); diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index a38cb16b..bbee9403 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -8,17 +8,18 @@ namespace dxvk { const Rc& vkd, const Rc& 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 DxvkDevice::createContext() { return new DxvkContext(this, m_pipelineManager, - m_metaClearObjects); + m_metaClearObjects, + m_metaMipGenObjects); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 9ce2f42f..d7c60652 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -356,6 +356,7 @@ namespace dxvk { Rc m_renderPassPool; Rc m_pipelineManager; Rc m_metaClearObjects; + Rc m_metaMipGenObjects; DxvkUnboundResources m_unboundResources; From b30e53fa0d204c72a6d43711ccc582344b174432 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 19:15:14 +0200 Subject: [PATCH 31/46] [dxvk] Track image (rather than the view) when generating mip maps --- src/dxvk/dxvk_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index f440ef64..64a15484 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -1073,7 +1073,7 @@ namespace dxvk { } m_cmd->trackResource(mipGenerator); - m_cmd->trackResource(imageView); + m_cmd->trackResource(imageView->image()); } From a0e0ba1cc8f62ff2f9ea8cac4415ad268447e8c6 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 21:02:15 +0200 Subject: [PATCH 32/46] [dxvk] Align length when clearing entire buffer Buffer slices in DXVK are always aligned to at least 256 bytes, so this is safe. Fixes a regression leading to GPU hangs on RADV. --- src/dxvk/dxvk_context.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 64a15484..72ef7f3b 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -230,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( From b78130defd4b3b19543ed4ce1244e607131814dc Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 25 May 2018 23:53:34 +0200 Subject: [PATCH 33/46] [d3d11] Overwatch: Fake success in CreateGeometryShaderWithStreamOutput --- src/d3d11/d3d11_device.cpp | 5 ++++- src/d3d11/d3d11_options.cpp | 3 ++- src/d3d11/d3d11_options.h | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index b576143e..aca5642e 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -1115,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; } diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index 613a27c1..e5ac4841 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -5,8 +5,9 @@ namespace dxvk { const static std::unordered_map g_d3d11AppOptions = {{ - { "Dishonored2.exe", D3D11OptionSet(D3D11Option::AllowMapFlagNoWait) }, + { "Dishonored2.exe", D3D11OptionSet(D3D11Option::AllowMapFlagNoWait) }, { "Fallout4.exe", D3D11OptionSet(D3D11Option::DisableGetDataFlagDoNotFlush) }, + { "Overwatch.exe", D3D11OptionSet(D3D11Option::FakeStreamOutSupport) }, }}; diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index 97e8a603..c0c6b2e1 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -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; From 27816b470a958c896d6bb679eb39ca7430ab9120 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 13:52:33 +0200 Subject: [PATCH 34/46] [spirv] Add support for 32-bit specialization constants --- src/spirv/spirv_module.cpp | 13 +++++++++++++ src/spirv/spirv_module.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index 87d211df..c6262f5c 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -340,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) { diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index febd46ca..47f12f8e 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -158,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); From 4ae15f3edf5a1e39d11eb68220720481b0871bc1 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 14:50:00 +0200 Subject: [PATCH 35/46] [dxvk] Add global specialization constant data structures Implements a unified way of defining specialization constants for graphics and compute pipelines which can be easily extended in the future. --- src/dxvk/dxvk_shader.h | 16 ++++++++ src/dxvk/dxvk_spec_const.cpp | 43 +++++++++++++++++++++ src/dxvk/dxvk_spec_const.h | 72 ++++++++++++++++++++++++++++++++++++ src/dxvk/meson.build | 1 + 4 files changed, 132 insertions(+) create mode 100644 src/dxvk/dxvk_spec_const.cpp create mode 100644 src/dxvk/dxvk_spec_const.h diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index fc6fb4eb..0aa092dc 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -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 * diff --git a/src/dxvk/dxvk_spec_const.cpp b/src/dxvk/dxvk_spec_const.cpp new file mode 100644 index 00000000..8d2565de --- /dev/null +++ b/src/dxvk/dxvk_spec_const.cpp @@ -0,0 +1,43 @@ +#include "dxvk_spec_const.h" + +#include + +#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; + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_spec_const.h b/src/dxvk/dxvk_spec_const.h new file mode 100644 index 00000000..445fba9e --- /dev/null +++ b/src/dxvk/dxvk_spec_const.h @@ -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 m_mapEntries; + + void setConstantEntry( + DxvkSpecConstantId specId, + uint32_t offset, + uint32_t size); + + void setBindingEntry( + uint32_t binding); + + }; + + extern DxvkSpecConstantMap g_specConstantMap; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index e2a5fabf..3c21bb1b 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -61,6 +61,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', From d79f39b963168396fbd8e9dbc33fc0089bff5e5b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 14:54:05 +0200 Subject: [PATCH 36/46] [dxbc] Implement sampleinfo instruction for rasterizer --- src/dxbc/dxbc_compiler.cpp | 68 +++++++++++++++++++++++++++++++++----- src/dxbc/dxbc_compiler.h | 29 ++++++++++++++++ 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 8d918b24..b3b83136 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -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(); } @@ -4557,15 +4564,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; + } } @@ -4778,6 +4791,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 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); diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 7f82498a..b734d4ec 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -96,6 +96,20 @@ 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 Vertex shader-specific structure */ @@ -384,6 +398,13 @@ namespace dxvk { // currently active if-else blocks and loops. std::vector m_controlFlowBlocks; + /////////////////////////////////////////////// + // Specialization constants. These are defined + // as needed by the getSpecConstant method. + std::array m_specConstants; + /////////////////////////////////////////////////////////// // Array of input values. Since v# registers are indexable // in DXBC, we need to copy them into an array first. @@ -848,6 +869,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(); From a2d9874b26a57de512f7c31990051d8c495a4d63 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 14:54:29 +0200 Subject: [PATCH 37/46] [dxvk] Use new spec constant structure for pipeline compilation --- src/dxvk/dxvk_compute.cpp | 18 ++++++++---------- src/dxvk/dxvk_graphics.cpp | 19 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index ee4f5daa..7f45fe23 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -3,6 +3,7 @@ #include "dxvk_compute.h" #include "dxvk_device.h" +#include "dxvk_spec_const.h" namespace dxvk { @@ -99,19 +100,16 @@ namespace dxvk { Logger::debug(str::format(" cs : ", m_cs->shader()->debugName())); } - std::array specData; - std::array 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(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; diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 4d2e9f6e..844f676d 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -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 specData; - std::array 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(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 stages; From 667616bc39a3d83aec0ff9c425781c62640772e2 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 14:56:36 +0200 Subject: [PATCH 38/46] [dxvk] Remove unnecessary include --- src/dxvk/dxvk_spec_const.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dxvk/dxvk_spec_const.cpp b/src/dxvk/dxvk_spec_const.cpp index 8d2565de..ab4ade66 100644 --- a/src/dxvk/dxvk_spec_const.cpp +++ b/src/dxvk/dxvk_spec_const.cpp @@ -1,7 +1,5 @@ #include "dxvk_spec_const.h" -#include - #define SET_CONSTANT_ENTRY(specId, member) \ this->setConstantEntry(specId, \ offsetof(DxvkSpecConstantData, member), \ From ec5572aa69d5749cba3ad0a5e1b76109851634b7 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 17:08:08 +0200 Subject: [PATCH 39/46] [dxbc] Scan for discard and derivative instructions --- src/dxbc/dxbc_analysis.cpp | 12 +++++++++++- src/dxbc/dxbc_analysis.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/dxbc/dxbc_analysis.cpp b/src/dxbc/dxbc_analysis.cpp index c9c98017..a230da97 100644 --- a/src/dxbc/dxbc_analysis.cpp +++ b/src/dxbc/dxbc_analysis.cpp @@ -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; diff --git a/src/dxbc/dxbc_analysis.h b/src/dxbc/dxbc_analysis.h index d51ede59..5059ae84 100644 --- a/src/dxbc/dxbc_analysis.h +++ b/src/dxbc/dxbc_analysis.h @@ -37,6 +37,9 @@ namespace dxvk { DxbcClipCullInfo clipCullIn; DxbcClipCullInfo clipCullOut; + + bool usesDerivatives = false; + bool usesKill = false; }; /** From af62d423b26f0b3d4a318c2490b1bf4758c4727f Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 17:08:39 +0200 Subject: [PATCH 40/46] [dxbc] Add per-device option for deferred kills --- src/dxbc/dxbc_options.cpp | 1 + src/dxbc/dxbc_options.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index 6ccfd565..4a1621b8 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -35,6 +35,7 @@ namespace dxvk { if (devFeatures.shaderStorageImageReadWithoutFormat) flags.set(DxbcOption::UseStorageImageReadWithoutFormat); + flags.set(DxbcOption::DeferKill); return flags; } diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index 1a8e575c..72bedd56 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -17,6 +17,11 @@ namespace dxvk { /// Use FMin/FMax/FClamp instead of NMin/NMax/NClamp. /// Workaround for bugs in older Nvidia drivers. UseSimpleMinMaxClamp, + + /// 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; From 92782214168ca9aa1c2f398374a750884019e074 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 17:46:49 +0200 Subject: [PATCH 41/46] [dxbc] Run analyzer before creating compiler object We may need the analysis results when initializing the compiler object. --- src/dxbc/dxbc_module.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dxbc/dxbc_module.cpp b/src/dxbc/dxbc_module.cpp index f70ab68e..4ac55266 100644 --- a/src/dxbc/dxbc_module.cpp +++ b/src/dxbc/dxbc_module.cpp @@ -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(); From 0619842dd958802a309d39e71540a76a15a30bf8 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 19:00:22 +0200 Subject: [PATCH 42/46] [dxbc] Make UAV stores and atomics conditional We should only perform stores when the corresponding UAV is bound. This may be extended with range checks at a later time. --- src/dxbc/dxbc_compiler.cpp | 89 +++++++++++++++++++++++++++++++++++--- src/dxbc/dxbc_compiler.h | 14 ++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index b3b83136..dd098c9b 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -2003,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 = bufferInfo.specId; + + 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]); @@ -2126,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); + } } @@ -2133,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 = bufferInfo.specId; + + 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; @@ -2187,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); } @@ -3257,6 +3298,18 @@ namespace dxvk { const uint32_t registerId = ins.dst[0].idx[0].offset; const DxbcUav uavInfo = m_uavs.at(registerId); + // Execute write op only if the UAV is bound + uint32_t writeTest = uavInfo.specId; + + 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); @@ -3271,6 +3324,10 @@ namespace dxvk { m_module.opImageWrite( m_module.opLoad(uavInfo.imageTypeId, uavInfo.varId), texCoord.id, texValue.id, SpirvImageOperands()); + + // End conditional block + m_module.opBranch(cond.labelEnd); + m_module.opLabel (cond.labelEnd); } @@ -4467,11 +4524,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 = bufferInfo.specId; + + 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 }); @@ -4520,6 +4593,12 @@ namespace dxvk { srcComponentIndex += 1; } } + + // End conditional block + if (isUav) { + m_module.opBranch(cond.labelEnd); + m_module.opLabel (cond.labelEnd); + } } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index b734d4ec..969559c6 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -110,6 +110,20 @@ namespace dxvk { }; + /** + * \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 */ From 001794a094fa9004cc564ffe1392d4e9f2986c58 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 19:25:20 +0200 Subject: [PATCH 43/46] [dxbc] Implement deferred kill operation Fixes visual issues during LOD transitions in The Witcher 3, Rise of the Tomb Raider and other games with a similar LOD system. UAV stores and atomics are conditionalized as well. --- src/dxbc/dxbc_compiler.cpp | 96 ++++++++++++++++++++++++++++---------- src/dxbc/dxbc_compiler.h | 7 +++ 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index dd098c9b..abeb1658 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -2013,7 +2013,7 @@ namespace dxvk { DxbcConditional cond; if (isUav) { - uint32_t writeTest = bufferInfo.specId; + uint32_t writeTest = emitUavWriteTest(bufferInfo); cond.labelIf = m_module.allocateId(); cond.labelEnd = m_module.allocateId(); @@ -2165,7 +2165,7 @@ namespace dxvk { m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId); // Only perform the operation if the UAV is bound - uint32_t writeTest = bufferInfo.specId; + uint32_t writeTest = emitUavWriteTest(bufferInfo); DxbcConditional cond; cond.labelIf = m_module.allocateId(); @@ -3295,11 +3295,10 @@ 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 = uavInfo.specId; + uint32_t writeTest = emitUavWriteTest(uavInfo); DxbcConditional cond; cond.labelIf = m_module.allocateId(); @@ -3311,18 +3310,17 @@ namespace dxvk { 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 @@ -3667,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); + } } @@ -4532,7 +4535,7 @@ namespace dxvk { DxbcConditional cond; if (isUav) { - uint32_t writeTest = bufferInfo.specId; + uint32_t writeTest = emitUavWriteTest(bufferInfo); cond.labelIf = m_module.allocateId(); cond.labelEnd = m_module.allocateId(); @@ -5552,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); @@ -5756,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"); @@ -5853,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(); } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 969559c6..38115a08 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -165,6 +165,8 @@ namespace dxvk { uint32_t builtinSampleMaskIn = 0; uint32_t builtinSampleMaskOut = 0; uint32_t builtinLayer = 0; + + uint32_t killState = 0; }; @@ -954,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(); From 97e3b89bc7def8775cecad38a6d9fc3d2de50a4b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 20:09:31 +0200 Subject: [PATCH 44/46] [dxvk] Emit dynamic state only if a pipeline is bound Fixes validation errors in Dirt 4. --- src/dxvk/dxvk_context.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 72ef7f3b..87205d0f 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -1383,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); } @@ -1399,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); + } } From 48037a8b87d3e7a54f631303d42eace603617580 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 26 May 2018 20:34:40 +0200 Subject: [PATCH 45/46] [d3d11] Enable shaderStorageImageMultisample if the device supports it --- src/d3d11/d3d11_device.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index aca5642e..4ad4c8c7 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -1807,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; } From 8cd97959f2b97c17d25ce2b8b1956a6a08ebe893 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 27 May 2018 01:10:49 +0200 Subject: [PATCH 46/46] [d3d11] Chjeck whether input layouts are identical Prevents redundant state changes when a game switches between identical input layouts. Reduces the the number of Vulkan calls in Grim Dawn by ~30%. --- src/d3d11/d3d11_context.cpp | 12 +++++++++++- src/d3d11/d3d11_input_layout.cpp | 31 ++++++++++++++++++++++++++----- src/d3d11/d3d11_input_layout.h | 21 ++++++++++++--------- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index e7ab7e9e..62bb9844 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -1235,8 +1235,18 @@ namespace dxvk { auto inputLayout = static_cast(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(); } } diff --git a/src/d3d11/d3d11_input_layout.cpp b/src/d3d11/d3d11_input_layout.cpp index a19aa915..2e4e794f 100644 --- a/src/d3d11/d3d11_input_layout.cpp +++ b/src/d3d11/d3d11_input_layout.cpp @@ -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; + } + } diff --git a/src/d3d11/d3d11_input_layout.h b/src/d3d11/d3d11_input_layout.h index cfe068d3..e3d33d1d 100644 --- a/src/d3d11/d3d11_input_layout.h +++ b/src/d3d11/d3d11_input_layout.h @@ -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& ctx); + const Rc& ctx); + + bool Compare( + const D3D11InputLayout* pOther) const; private: